Compare commits

...

5 Commits

Author SHA1 Message Date
f68deab8c0 Fix App Header
Layouts & Navigation
- app/(application)/_layout.tsx
- app/(application)/(user)/_layout.tsx
- app/(application)/(user)/event/(tabs)/_layout.tsx
- app/(application)/(user)/job/(tabs)/_layout.tsx
- app/(application)/(user)/voting/(tabs)/_layout.tsx
- app/(application)/(user)/portofolio/_layout.tsx
- app/(application)/(user)/profile/_layout.tsx
- app/(application)/admin/_layout.tsx
- app/+not-found.tsx

User – File
- app/(application)/(file)/[id].tsx

User – Collaboration
- app/(application)/(user)/collaboration/[id]/index.tsx
- app/(application)/(user)/collaboration/[id]/detail-project-main.tsx
- app/(application)/(user)/collaboration/[id]/detail-participant.tsx
- app/(application)/(user)/collaboration/[id]/[detail]/info.tsx
- app/(application)/(user)/collaboration/[id]/[detail]/room-chat.tsx

User – Donation
- app/(application)/(user)/donation/[id]/index.tsx
- app/(application)/(user)/donation/[id]/[status]/detail.tsx
- app/(application)/(user)/donation/[id]/(news)/[news]/index.tsx

User – Event
- app/(application)/(user)/event/[id]/[status]/detail-event.tsx
- app/(application)/(user)/event/[id]/confirmation.tsx
- app/(application)/(user)/event/[id]/contribution.tsx
- app/(application)/(user)/event/[id]/history.tsx
- app/(application)/(user)/event/[id]/publish.tsx

User – Investment
- app/(application)/(user)/investment/[id]/index.tsx
- app/(application)/(user)/investment/[id]/[status]/detail.tsx
- app/(application)/(user)/investment/[id]/(my-holding)/[id].tsx
- app/(application)/(user)/investment/[id]/(news)/[news]/index.tsx

User – Job
- app/(application)/(user)/job/[id]/[status]/detail.tsx

User – Portofolio
- app/(application)/(user)/portofolio/[id]/index.tsx

User – Profile
- app/(application)/(user)/profile/[id]/index.tsx

User – Voting
- app/(application)/(user)/voting/[id]/index.tsx
- app/(application)/(user)/voting/[id]/[status]/detail.tsx
- app/(application)/(user)/voting/[id]/contribution.tsx
- app/(application)/(user)/voting/[id]/history.tsx

Components
- components/Button/BackButtonFromNotification.tsx
- components/_ShareComponent/AppHeader.tsx

Admin Screens
- screens/Admin/Notification-Admin/ScreenNotificationAdmin.tsx
- screens/Admin/Notification-Admin/ScreenNotificationAdmin2.tsx

Screens – Donation
- screens/Donation/ScreenListOfNews.tsx
- screens/Donation/ScreenRecapOfNews.tsx

Screens – Forum
- screens/Forum/ViewBeranda.tsx
- screens/Forum/ViewBeranda2.tsx
- screens/Forum/ViewBeranda3.tsx

Screens – Investment
- screens/Invesment/Document/ScreenRecapOfDocument.tsx
- screens/Invesment/News/ScreenListOfNews.tsx
- screens/Invesment/News/ScreenRecapOfNews.tsx

Screens – Notification
- screens/Notification/ScreenNotification_V1.tsx
- screens/Notification/ScreenNotification_V2.tsx

iOS
- ios/HIPMIBadungConnect.xcodeproj/project.pbxproj

Docs
- QWEN.md

### Issue: Tabs can't clicked
2026-03-13 16:41:34 +08:00
37d2fbe48a Fix code/
User Pages
- app/(application)/(user)/home.tsx
- app/(application)/(user)/portofolio/[id]/create.tsx

Components
- components/_ShareComponent/AppHeader.tsx

Screens
- screens/Portofolio/ScreenPortofolioCreate.tsx

Config & Dependencies
- app.config.js
- package.json
- bun.lock

iOS
- ios/HIPMIBadungConnect.xcodeproj/project.pbxproj
- ios/HIPMIBadungConnect/Info.plist

Docs
- docs/prompt-for-qwen-code.md

### No issue
2026-03-12 16:50:02 +08:00
57c9215771 Feat validasi mobile otp
modified:   context/AuthContext.tsx
        modified:   screens/Authentication/VerificationView.tsx
        modified:   service/api-config.ts

### No Issue
2026-03-11 15:04:32 +08:00
4efdbd3c7b feat: update admin features, user confirmation, and native configs
- Admin: Update layout, notification bell, and event detail screen
- User: Improve event confirmation flow
- Config: Update AndroidManifest, Info.plist, entitlements, and app.config.js

### No Issue
2026-03-11 11:29:20 +08:00
ad32eb6fe6 feat: implement deep linking & universal links for event confirmation
- Add QR code toggle for HTTPS/Custom Scheme links
- Add Universal Links (iOS) and App Links (Android) support
- Create deep link route handler with platform detection
- Add .well-known files for domain verification
- Fix Content-Type headers for apple-app-site-association
- Add environment configuration for staging & production
- Add comprehensive testing documentation

Testing:
- QR scan → Safari → App switch working 
- Platform detection working 
- Auto redirect to custom scheme working 
- Web fallback JSON response working 

### No Issue
2026-03-09 16:39:01 +08:00
65 changed files with 1804 additions and 943 deletions

View File

@@ -387,7 +387,7 @@ apiConfig.interceptors.request.use(async (config) => {
### Deep Linking ### Deep Linking
- Scheme: `hipmimobile://` - Scheme: `hipmimobile://`
- HTTPS: `cld-dkr-staging-hipmi.wibudev.com` - HTTPS: `cld-dkr-hipmi-stg.wibudev.com`
- Configured for both platforms - Configured for both platforms
### Camera ### Camera

View File

@@ -37,7 +37,7 @@
</intent-filter> </intent-filter>
<intent-filter android:autoVerify="true" data-generated="true"> <intent-filter android:autoVerify="true" data-generated="true">
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW"/>
<data android:scheme="https" android:host="cld-dkr-staging-hipmi.wibudev.com" android:pathPrefix="/"/> <data android:scheme="https" android:host="cld-dkr-hipmi-stg.wibudev.com" android:pathPrefix="/"/>
<category android:name="android.intent.category.BROWSABLE"/> <category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
</intent-filter> </intent-filter>

View File

@@ -1,6 +1,17 @@
// app.config.js // app.config.js
require("dotenv").config(); require("dotenv").config();
// const isDev = process.env.NODE_ENV === "development";
// const isStaging = process.env.NEXT_PUBLIC_ENV === "staging";
// const isProd = process.env.NEXT_PUBLIC_ENV === "production";
// Domain berdasarkan environment
// const domain = isDev
// ? "localhost:3000"
// : isStaging
// ? "cld-dkr-hipmi-stg.wibudev.com"
// : "hipmi.muku.id"; // Production domain
export default { export default {
name: "HIPMI Badung Connect", name: "HIPMI Badung Connect",
slug: "hipmi-mobile", slug: "hipmi-mobile",
@@ -20,8 +31,10 @@ export default {
NSLocationWhenInUseUsageDescription: NSLocationWhenInUseUsageDescription:
"Aplikasi membutuhkan akses lokasi untuk menampilkan peta.", "Aplikasi membutuhkan akses lokasi untuk menampilkan peta.",
}, },
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"], associatedDomains: [
buildNumber: "3", "applinks:cld-dkr-hipmi-stg.wibudev.com",
],
buildNumber: "5",
}, },
android: { android: {
@@ -41,7 +54,7 @@ export default {
data: [ data: [
{ {
scheme: "https", scheme: "https",
host: "cld-dkr-staging-hipmi.wibudev.com", host: "cld-dkr-hipmi-stg.wibudev.com",
pathPrefix: "/", pathPrefix: "/",
}, },
], ],

View File

@@ -1,4 +1,5 @@
import { BackButton } from "@/components"; import { BackButton } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import PdfViewer from "@/components/_ShareComponent/PdfViewer"; import PdfViewer from "@/components/_ShareComponent/PdfViewer";
import API_STRORAGE from "@/constants/base-url-api-strorage"; import API_STRORAGE from "@/constants/base-url-api-strorage";
import { Stack, useLocalSearchParams } from "expo-router"; import { Stack, useLocalSearchParams } from "expo-router";
@@ -7,13 +8,12 @@ import { SafeAreaView } from "react-native-safe-area-context";
export default function FileScreen() { export default function FileScreen() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const url = API_STRORAGE.GET({ fileId: id as string }); const url = API_STRORAGE.GET({ fileId: id as string });
return ( return (
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "File", header: () => <AppHeader title="File" left={<BackButton />} />,
headerLeft: () => <BackButton />,
}} }}
/> />
<SafeAreaView style={{ flex: 1 }} edges={["bottom"]}> <SafeAreaView style={{ flex: 1 }} edges={["bottom"]}>

View File

@@ -1,22 +1,21 @@
import { BackButton } from "@/components"; import { BackButton } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconPlus } from "@/components/_Icon"; import { IconPlus } from "@/components/_Icon";
import { IconDot } from "@/components/_Icon/IconComponent"; import { IconDot } from "@/components/_Icon/IconComponent";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value"; import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { HeaderStyles } from "@/styles/header-styles";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { router, Stack } from "expo-router"; import { router, Stack } from "expo-router";
export default function UserLayout() { export default function UserLayout() {
return ( return (
<> <>
<Stack screenOptions={HeaderStyles}> <Stack>
<Stack.Screen <Stack.Screen
name="delete-account" name="delete-account"
options={{ options={{
title: "Hapus Akun", header: () => <AppHeader title="Hapus Akun" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
@@ -47,8 +46,7 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="user-search/index" name="user-search/index"
options={{ options={{
title: "Pencarian Pengguna", header: () => <AppHeader title="Pencarian Pengguna" />,
headerLeft: () => <BackButton />,
}} }}
/> />
@@ -71,10 +69,18 @@ export default function UserLayout() {
{/* ========== Event Section ========= */} {/* ========== Event Section ========= */}
{/* <Stack.Screen
name="event/(tabs)"
options={{
header: () => <AppHeader title="Event" left={<BackButton path="/home" />} />,
}}
/> */}
<Stack.Screen <Stack.Screen
name="event/(tabs)" name="event/(tabs)"
options={{ options={{
title: "Event", title: "Event",
header: () => <AppHeader title="Event" left={<BackButton path="/home" />} />,
// NOTE: DIPINDAH DI FILE /Event/(Tabs)/_layout.tsx // NOTE: DIPINDAH DI FILE /Event/(Tabs)/_layout.tsx
// headerLeft: () => ( // headerLeft: () => (
// <LeftButtonCustom path="/(application)/(user)/home" /> // <LeftButtonCustom path="/(application)/(user)/home" />
@@ -85,32 +91,28 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="event/create" name="event/create"
options={{ options={{
title: "Tambah Event", header: () => <AppHeader title="Tambah Event" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="event/detail/[id]" name="event/detail/[id]"
options={{ options={{
title: "Event Detail", header: () => <AppHeader title="Event Detail" left={<LeftButtonCustom />} />,
headerLeft: () => <LeftButtonCustom />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="event/[id]/edit" name="event/[id]/edit"
options={{ options={{
title: "Edit Event", header: () => <AppHeader title="Edit Event" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="event/[id]/list-of-participants" name="event/[id]/list-of-participants"
options={{ options={{
title: "Daftar peserta", header: () => <AppHeader title="Daftar peserta" />,
headerLeft: () => <BackButton />,
}} }}
/> />
{/* ========== End Event Section ========= */} {/* ========== End Event Section ========= */}
@@ -119,22 +121,19 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="collaboration/(tabs)" name="collaboration/(tabs)"
options={{ options={{
title: "Collaboration", header: () => <AppHeader title="Collaboration" left={<BackButton path="/home" />} />,
headerLeft: () => <BackButton path="/home" />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="collaboration/create" name="collaboration/create"
options={{ options={{
title: "Tambah Proyek", header: () => <AppHeader title="Tambah Proyek" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="collaboration/[id]/list-of-participants" name="collaboration/[id]/list-of-participants"
options={{ options={{
title: "Daftar Partisipan", header: () => <AppHeader title="Daftar Partisipan" />,
headerLeft: () => <BackButton />,
}} }}
/> />
{/* <Stack.Screen {/* <Stack.Screen
@@ -147,22 +146,19 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="collaboration/[id]/edit" name="collaboration/[id]/edit"
options={{ options={{
title: "Edit Proyek", header: () => <AppHeader title="Edit Proyek" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="collaboration/[id]/create-pacticipants" name="collaboration/[id]/create-pacticipants"
options={{ options={{
title: "Ajukan Partisipasi", header: () => <AppHeader title="Ajukan Partisipasi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="collaboration/[id]/select-of-participants" name="collaboration/[id]/select-of-participants"
options={{ options={{
title: "Pilih Partisipan", header: () => <AppHeader title="Pilih Partisipan" />,
headerLeft: () => <BackButton />,
}} }}
/> />
@@ -172,29 +168,25 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="voting/create" name="voting/create"
options={{ options={{
title: "Tambah Voting", header: () => <AppHeader title="Tambah Voting" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="voting/(tabs)" name="voting/(tabs)"
options={{ options={{
title: "Voting", header: () => <AppHeader title="Voting" left={<BackButton path="/home" />} />,
headerLeft: () => <BackButton path="/home" />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="voting/[id]/edit" name="voting/[id]/edit"
options={{ options={{
title: "Edit Voting", header: () => <AppHeader title="Edit Voting" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="voting/[id]/list-of-contributor" name="voting/[id]/list-of-contributor"
options={{ options={{
title: "Daftar Kontributor", header: () => <AppHeader title="Daftar Kontributor" />,
headerLeft: () => <BackButton />,
}} }}
/> />
@@ -204,8 +196,7 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="crowdfunding/index" name="crowdfunding/index"
options={{ options={{
title: "Crowdfunding", header: () => <AppHeader title="Crowdfunding" left={<BackButton path="/home" />} />,
headerLeft: () => <BackButton path="/home" />,
}} }}
/> />
@@ -215,103 +206,95 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="investment/(tabs)" name="investment/(tabs)"
options={{ options={{
title: "Investasi", header: () => <AppHeader title="Investasi" left={<BackButton path="/crowdfunding" />} />,
headerLeft: () => <BackButton path="/crowdfunding" />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/create" name="investment/create"
options={{ options={{
title: "Tambah Investasi", header: () => <AppHeader title="Tambah Investasi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/index" name="investment/[id]/index"
options={{ options={{
title: "Detail Investasi", header: () => <AppHeader title="Detail Investasi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/edit" name="investment/[id]/edit"
options={{ options={{
title: "Edit Investasi", header: () => <AppHeader title="Edit Investasi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/edit-prospectus" name="investment/[id]/edit-prospectus"
options={{ options={{
title: "Edit Prospektus", header: () => <AppHeader title="Edit Prospektus" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/(document)/list-of-document" name="investment/[id]/(document)/list-of-document"
options={{ options={{
title: "Daftar Dokumen", header: () => <AppHeader title="Daftar Dokumen" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/(document)/add-document" name="investment/[id]/(document)/add-document"
options={{ options={{
title: "Tambah Dokumen", header: () => <AppHeader title="Tambah Dokumen" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/(document)/edit-document" name="investment/[id]/(document)/edit-document"
options={{ options={{
title: "Edit Dokumen", header: () => <AppHeader title="Edit Dokumen" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/(news)/add-news" name="investment/[id]/(news)/add-news"
options={{ options={{
title: "Tambah Berita", header: () => <AppHeader title="Tambah Berita" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/investor" name="investment/[id]/investor"
options={{ options={{
title: "Investor", header: () => <AppHeader title="Investor" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/(transaction-flow)/index" name="investment/[id]/(transaction-flow)/index"
options={{ options={{
title: "Pembelian Saham", header: () => <AppHeader title="Pembelian Saham" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/(transaction-flow)/select-bank" name="investment/[id]/(transaction-flow)/select-bank"
options={{ options={{
title: "Pilih Bank", header: () => <AppHeader title="Pilih Bank" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/(transaction-flow)/invoice" name="investment/[id]/(transaction-flow)/invoice"
options={{ options={{
title: "Invoice", header: () => (
headerLeft: () => ( <AppHeader
<Ionicons title="Invoice"
name="close" left={
size={ICON_SIZE_SMALL} <Ionicons
color={MainColor.yellow} name="close"
onPress={() => size={ICON_SIZE_SMALL}
router.navigate(`/investment/(tabs)/transaction`) color={MainColor.yellow}
onPress={() =>
router.navigate(`/investment/(tabs)/transaction`)
}
/>
} }
/> />
), ),
@@ -320,14 +303,18 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="investment/[id]/(transaction-flow)/process" name="investment/[id]/(transaction-flow)/process"
options={{ options={{
title: "Proses", header: () => (
headerLeft: () => ( <AppHeader
<Ionicons title="Proses"
name="close" left={
size={ICON_SIZE_SMALL} <Ionicons
color={MainColor.yellow} name="close"
onPress={() => size={ICON_SIZE_SMALL}
router.navigate(`/investment/(tabs)/transaction`) color={MainColor.yellow}
onPress={() =>
router.navigate(`/investment/(tabs)/transaction`)
}
/>
} }
/> />
), ),
@@ -336,23 +323,20 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="investment/[id]/(transaction-flow)/success" name="investment/[id]/(transaction-flow)/success"
options={{ options={{
title: "Transaksi Berhasil", header: () => <AppHeader title="Transaksi Berhasil" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/(transaction-flow)/failed" name="investment/[id]/(transaction-flow)/failed"
options={{ options={{
title: "Transaksi Gagal", header: () => <AppHeader title="Transaksi Gagal" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="investment/[id]/(my-holding)/[id]" name="investment/[id]/(my-holding)/[id]"
options={{ options={{
title: "Detail Saham Saya", header: () => <AppHeader title="Detail Saham Saya" />,
headerLeft: () => <BackButton />,
}} }}
/> />
{/* ========== End Investment Section ========= */} {/* ========== End Investment Section ========= */}
@@ -361,122 +345,111 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="donation/(tabs)" name="donation/(tabs)"
options={{ options={{
title: "Donasi", header: () => <AppHeader title="Donasi" left={<BackButton path="/crowdfunding" />} />,
headerLeft: () => <BackButton path="/crowdfunding" />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/create" name="donation/create"
options={{ options={{
title: "Tambah Donasi", header: () => <AppHeader title="Tambah Donasi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/create-story" name="donation/create-story"
options={{ options={{
title: "Tambah Donasi", header: () => <AppHeader title="Tambah Donasi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/edit" name="donation/[id]/edit"
options={{ options={{
title: "Edit Donasi", header: () => <AppHeader title="Edit Donasi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/edit-story" name="donation/[id]/edit-story"
options={{ options={{
title: "Edit Donasi", header: () => <AppHeader title="Edit Donasi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/edit-rekening" name="donation/[id]/edit-rekening"
options={{ options={{
title: "Edit Rekening", header: () => <AppHeader title="Edit Rekening" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/detail-story" name="donation/[id]/detail-story"
options={{ options={{
title: "Cerita Penggalang", header: () => <AppHeader title="Cerita Penggalang" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/infromation-fundrising" name="donation/[id]/infromation-fundrising"
options={{ options={{
title: "Informasi Penggalang Dana", header: () => <AppHeader title="Informasi Penggalang Dana" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/list-of-donatur" name="donation/[id]/list-of-donatur"
options={{ options={{
title: "Daftar Donatur", header: () => <AppHeader title="Daftar Donatur" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/fund-disbursement" name="donation/[id]/fund-disbursement"
options={{ options={{
title: "Pencairan Dana", header: () => <AppHeader title="Pencairan Dana" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/(news)/recap-of-news" name="donation/[id]/(news)/recap-of-news"
options={{ options={{
title: "Rekap Kabar", header: () => <AppHeader title="Rekap Kabar" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/(news)/add-news" name="donation/[id]/(news)/add-news"
options={{ options={{
title: "Tambah Berita", header: () => <AppHeader title="Tambah Berita" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/(news)/[news]/edit-news" name="donation/[id]/(news)/[news]/edit-news"
options={{ options={{
title: "Edit Berita", header: () => <AppHeader title="Edit Berita" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/(transaction-flow)/index" name="donation/[id]/(transaction-flow)/index"
options={{ options={{
title: "Donasi", header: () => <AppHeader title="Donasi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/(transaction-flow)/select-bank" name="donation/[id]/(transaction-flow)/select-bank"
options={{ options={{
title: "Pilih Bank", header: () => <AppHeader title="Pilih Bank" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/(transaction-flow)/[invoiceId]/invoice" name="donation/[id]/(transaction-flow)/[invoiceId]/invoice"
options={{ options={{
title: "Invoice", header: () => (
headerLeft: () => ( <AppHeader
<Ionicons title="Invoice"
name="close" left={
size={ICON_SIZE_SMALL} <Ionicons
color={MainColor.yellow} name="close"
onPress={() => router.navigate(`/donation/(tabs)/my-donation`)} size={ICON_SIZE_SMALL}
color={MainColor.yellow}
onPress={() => router.navigate(`/donation/(tabs)/my-donation`)}
/>
}
/> />
), ),
}} }}
@@ -484,13 +457,17 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="donation/[id]/(transaction-flow)/[invoiceId]/process" name="donation/[id]/(transaction-flow)/[invoiceId]/process"
options={{ options={{
title: "Proses", header: () => (
headerLeft: () => ( <AppHeader
<Ionicons title="Proses"
name="close" left={
size={ICON_SIZE_SMALL} <Ionicons
color={MainColor.yellow} name="close"
onPress={() => router.navigate(`/donation/(tabs)/my-donation`)} size={ICON_SIZE_SMALL}
color={MainColor.yellow}
onPress={() => router.navigate(`/donation/(tabs)/my-donation`)}
/>
}
/> />
), ),
}} }}
@@ -498,55 +475,51 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="donation/[id]/(transaction-flow)/[invoiceId]/success" name="donation/[id]/(transaction-flow)/[invoiceId]/success"
options={{ options={{
title: "Donasi Berhasil", header: () => <AppHeader title="Donasi Berhasil" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="donation/[id]/(transaction-flow)/[invoiceId]/failed" name="donation/[id]/(transaction-flow)/[invoiceId]/failed"
options={{ options={{
title: "Donasi Gagal", header: () => <AppHeader title="Donasi Gagal" />,
headerLeft: () => <BackButton />,
}} }}
/> />
{/* ========== End Donation Section ========= */} {/* ========== End Donation Section ========= */}
{/* ========== Job Section ========= */} {/* ========== Job Section ========= */}
<Stack.Screen <Stack.Screen
name="job/create" name="job/create"
options={{ options={{
title: "Tambah Job", header: () => <AppHeader title="Tambah Job" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="job/(tabs)" name="job/(tabs)"
options={{ options={{
title: "Job Vacancy", title: "Job Vacancy",
// headerLeft: () => <BackButton path="/home" />,
// NOTE: headerLeft di pindahkan ke Tabs Layout // NOTE: headerLeft di pindahkan ke Tabs Layout
header: () => <AppHeader title="Job Vacancy" left={<BackButton path="/home" />} />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="job/[id]/index" name="job/[id]/index"
options={{ options={{
title: "Detail Job", header: () => <AppHeader title="Detail Job" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="job/[id]/edit" name="job/[id]/edit"
options={{ options={{
title: "Edit Job", header: () => <AppHeader title="Edit Job" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="job/[id]/archive" name="job/[id]/archive"
options={{ options={{
title: "Arsip Job", header: () => <AppHeader title="Arsip Job" />,
headerLeft: () => <BackButton />,
}} }}
/> />
@@ -556,78 +529,67 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="forum/create" name="forum/create"
options={{ options={{
title: "Tambah Diskusi", header: () => <AppHeader title="Tambah Diskusi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/[id]/edit" name="forum/[id]/edit"
options={{ options={{
title: "Edit Diskusi", header: () => <AppHeader title="Edit Diskusi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/[id]/forumku" name="forum/[id]/forumku"
options={{ options={{
title: "Forumku", header: () => <AppHeader title="Forumku" left={<BackButton icon={"close"} />} />,
headerLeft: () => <BackButton icon={"close"} />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/[id]/index" name="forum/[id]/index"
options={{ options={{
title: "Detail", header: () => <AppHeader title="Detail" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/[id]/report-commentar" name="forum/[id]/report-commentar"
options={{ options={{
title: "Laporkan Komentar", header: () => <AppHeader title="Laporkan Komentar" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/[id]/other-report-commentar" name="forum/[id]/other-report-commentar"
options={{ options={{
title: "Laporkan Komentar", header: () => <AppHeader title="Laporkan Komentar" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/[id]/report-posting" name="forum/[id]/report-posting"
options={{ options={{
title: "Laporkan Diskusi", header: () => <AppHeader title="Laporkan Diskusi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/[id]/other-report-posting" name="forum/[id]/other-report-posting"
options={{ options={{
title: "Laporkan Diskusi", header: () => <AppHeader title="Laporkan Diskusi" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/terms" name="forum/terms"
options={{ options={{
title: "Syarat & Ketentuan Forum", header: () => <AppHeader title="Syarat & Ketentuan Forum" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/[id]/preview-report-posting" name="forum/[id]/preview-report-posting"
options={{ options={{
title: "Laporan Postingan", header: () => <AppHeader title="Laporan Postingan" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="forum/[id]/preview-report-comment" name="forum/[id]/preview-report-comment"
options={{ options={{
title: "Laporan Komentar", header: () => <AppHeader title="Laporan Komentar" />,
headerLeft: () => <BackButton />,
}} }}
/> />
@@ -635,29 +597,25 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="maps/index" name="maps/index"
options={{ options={{
title: "Maps", header: () => <AppHeader title="Maps" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="maps/create" name="maps/create"
options={{ options={{
title: "Tambah Maps", header: () => <AppHeader title="Tambah Maps" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="maps/[id]/edit" name="maps/[id]/edit"
options={{ options={{
title: "Edit Maps", header: () => <AppHeader title="Edit Maps" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="maps/[id]/custom-pin" name="maps/[id]/custom-pin"
options={{ options={{
title: "Custom Pin Maps", header: () => <AppHeader title="Custom Pin Maps" />,
headerLeft: () => <BackButton />,
}} }}
/> />
@@ -665,8 +623,7 @@ export default function UserLayout() {
<Stack.Screen <Stack.Screen
name="marketplace/index" name="marketplace/index"
options={{ options={{
title: "Market Place", header: () => <AppHeader title="Market Place" />,
headerLeft: () => <BackButton />,
}} }}
/> />
</Stack> </Stack>

View File

@@ -10,6 +10,7 @@ import {
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { apiCollaborationGroup } from "@/service/api-client/api-collaboration"; import { apiCollaborationGroup } from "@/service/api-client/api-collaboration";
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router"; import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useState, useCallback } from "react"; import { useState, useCallback } from "react";
@@ -40,8 +41,7 @@ export default function CollaborationRoomInfo() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Info`, header: () => <AppHeader title="Info" left={<BackButton />} />,
headerLeft: () => <BackButton />,
}} }}
/> />

View File

@@ -1,4 +1,5 @@
import { BackButton } from "@/components"; import { BackButton } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value"; import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import ChatScreen from "@/screens/Collaboration/GroupChatSection"; import ChatScreen from "@/screens/Collaboration/GroupChatSection";
@@ -12,14 +13,18 @@ export default function CollaborationRoomChat() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Proyek ${detail}`, header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title={`Proyek ${detail}`}
<Feather left={<BackButton />}
name="info" right={
size={ICON_SIZE_SMALL} <Feather
color={MainColor.yellow} name="info"
onPress={() => router.push(`/collaboration/${id}/${detail}/info`)} size={ICON_SIZE_SMALL}
color={MainColor.yellow}
onPress={() => router.push(`/collaboration/${id}/${detail}/info`)}
/>
}
/> />
), ),
}} }}

View File

@@ -6,6 +6,7 @@ import {
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
ViewWrapper ViewWrapper
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection"; import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
import { apiCollaborationGetOne } from "@/service/api-client/api-collaboration"; import { apiCollaborationGetOne } from "@/service/api-client/api-collaboration";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
@@ -38,10 +39,14 @@ export default function CollaborationDetailParticipant() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Detail Proyek", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Detail Proyek"
<DotButton onPress={() => setOpenDrawerParticipant(true)} /> left={<BackButton />}
right={
<DotButton onPress={() => setOpenDrawerParticipant(true)} />
}
/>
), ),
}} }}
/> />

View File

@@ -8,6 +8,7 @@ import {
Spacing, Spacing,
ViewWrapper ViewWrapper
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconEdit } from "@/components/_Icon"; import { IconEdit } from "@/components/_Icon";
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection"; import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
import { import {
@@ -66,9 +67,13 @@ export default function CollaborationDetailProjectMain() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Proyek Saya", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />, title="Proyek Saya"
left={<BackButton />}
right={<DotButton onPress={() => setOpenDrawer(true)} />}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -9,6 +9,7 @@ import {
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection"; import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
import { import {
@@ -74,10 +75,14 @@ export default function CollaborationDetail() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Detail Proyek", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Detail Proyek"
<DotButton onPress={() => setOpenDrawerMenu(true)} /> left={<BackButton />}
right={
<DotButton onPress={() => setOpenDrawerMenu(true)} />
}
/>
), ),
}} }}
/> />

View File

@@ -11,6 +11,7 @@ import {
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconEdit } from "@/components/_Icon"; import { IconEdit } from "@/components/_Icon";
import { IconTrash } from "@/components/_Icon/IconTrash"; import { IconTrash } from "@/components/_Icon/IconTrash";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
@@ -57,12 +58,17 @@ export default function DonationNews() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Detail Kabar", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => title="Detail Kabar"
user?.id === data?.authorId && ( left={<BackButton />}
<DotButton onPress={() => setOpenDrawer(true)} /> right={
), user?.id === data?.authorId && (
<DotButton onPress={() => setOpenDrawer(true)} />
)
}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -7,6 +7,7 @@ import {
NewWrapper, NewWrapper,
Spacing, Spacing,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconEdit, IconNews } from "@/components/_Icon"; import { IconEdit, IconNews } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom"; import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
@@ -97,14 +98,19 @@ export default function DonasiDetailStatus() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail ${_.startCase(status as string)}`, header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => title={`Detail ${_.startCase(status as string)}`}
status === "draft" ? ( left={<BackButton />}
<DotButton onPress={() => setOpenDrawer(true)} /> right={
) : status === "publish" ? ( status === "draft" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} /> <DotButton onPress={() => setOpenDrawer(true)} />
) : null, ) : status === "publish" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} />
) : null
}
/>
),
}} }}
/> />
<NewWrapper <NewWrapper

View File

@@ -10,6 +10,7 @@ import {
StackCustom, StackCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconNews } from "@/components/_Icon"; import { IconNews } from "@/components/_Icon";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom"; import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
@@ -90,12 +91,17 @@ export default function DonasiDetailBeranda() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail Donasi`, header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => title="Detail Donasi"
user?.id === data?.Author?.id ? ( left={<BackButton />}
<DotButton onPress={() => setOpenDrawer(true)} /> right={
) : null, user?.id === data?.Author?.id ? (
<DotButton onPress={() => setOpenDrawer(true)} />
) : null
}
/>
),
}} }}
/> />
<NewWrapper footerComponent={buttonSection}> <NewWrapper footerComponent={buttonSection}>

View File

@@ -4,36 +4,34 @@ import {
IconHome, IconHome,
IconStatus, IconStatus,
} from "@/components/_Icon"; } from "@/components/_Icon";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification"; import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
import { TabsStyles } from "@/styles/tabs-styles"; import { TabsStyles } from "@/styles/tabs-styles";
import { router, Tabs, useLocalSearchParams, useNavigation } from "expo-router"; import { router, Tabs, useLocalSearchParams } from "expo-router";
import { useLayoutEffect } from "react";
export default function EventTabsLayout() { export default function EventTabsLayout() {
const navigation = useNavigation();
const { from, category } = useLocalSearchParams<{ const { from, category } = useLocalSearchParams<{
from?: string; from?: string;
category?: string; category?: string;
}>(); }>();
console.log("from", from);
console.log("category", category);
// Atur header secara dinamis
useLayoutEffect(() => {
navigation.setOptions({
headerLeft: () => (
<BackButtonFromNotification
from={from as string}
category={category as string}
/>
),
});
}, [from, router, navigation]);
return ( return (
<Tabs screenOptions={TabsStyles}> <Tabs
screenOptions={{
...TabsStyles,
header: () => (
<AppHeader
title="Event"
left={
<BackButtonFromNotification
from={from as string}
category={category as string}
/>
}
/>
),
}}
>
<Tabs.Screen <Tabs.Screen
name="index" name="index"
options={{ options={{

View File

@@ -10,6 +10,7 @@ import {
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import Event_ButtonStatusSection from "@/screens/Event/ButtonStatusSection"; import Event_ButtonStatusSection from "@/screens/Event/ButtonStatusSection";
@@ -81,12 +82,17 @@ export default function EventDetailStatus() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail ${status === "publish" ? "" : status}`, header: () => (
headerLeft: () => <LeftButtonCustom />, <AppHeader
headerRight: () => title={`Detail ${status === "publish" ? "" : status}`}
status === "draft" ? ( left={<LeftButtonCustom />}
<DotButton onPress={() => setOpenDrawer(true)} /> right={
) : null, status === "draft" ? (
<DotButton onPress={() => setOpenDrawer(true)} />
) : null
}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -9,7 +9,8 @@ import {
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { AccentColor, MainColor } from "@/constants/color-palet"; import AppHeader from "@/components/_ShareComponent/AppHeader";
import { MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { import {
apiEventConfirmationAction, apiEventConfirmationAction,
@@ -60,7 +61,7 @@ export default function UserEventConfirmation() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
checkTokenAndDataParticipants() || console.log("Token is null"); checkTokenAndDataParticipants() || console.log("Token is null");
}, [token, id, user?.id]) }, [token, id, user?.id]),
); );
const checkTokenAndDataParticipants = async () => { const checkTokenAndDataParticipants = async () => {
@@ -113,7 +114,7 @@ export default function UserEventConfirmation() {
confirmationStart, confirmationStart,
confirmationEnd, confirmationEnd,
null, null,
"[]" "[]",
); );
// --- [4] Status waktu event (untuk pesan UI) --- // --- [4] Status waktu event (untuk pesan UI) ---
@@ -218,9 +219,14 @@ export default function UserEventConfirmation() {
if (isWithinConfirmationWindow) { if (isWithinConfirmationWindow) {
if (konfirmasi === false) { if (konfirmasi === false) {
return ( return (
<TamplateBox data={data}> // <TamplateBox data={data}>
<TamplateText text="Konfirmasi Kehadiran" /> // <TamplateText text="Konfirmasi Kehadiran" />
</TamplateBox> // </TamplateBox>
<UserParticipan_And_DuringEvent
id={data.id}
userId={user?.id as string}
data={data}
/>
); );
} }
return ( return (
@@ -260,18 +266,20 @@ export default function UserEventConfirmation() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Konfirmasi Event", header: () => (
// headerLeft: () => ( <AppHeader
// <Ionicons title="Konfirmasi Event"
// name="arrow-back" left={
// size={20} <Ionicons
// color={MainColor.yellow} name="arrow-back"
// onPress={() => size={20}
// router.navigate("/(application)/(user)/event/create") color={MainColor.yellow}
// } onPress={() => router.navigate("/")}
// /> />
// ), }
}} />
),
}}
/> />
<ViewWrapper>{handlerReturn()}</ViewWrapper> <ViewWrapper>{handlerReturn()}</ViewWrapper>
</> </>
@@ -497,7 +505,6 @@ const UserNotParticipan_And_DuringEvent = ({
); );
}; };
// 🟡 ZONA ACARA BERLANGSUN // 🟡 ZONA ACARA BERLANGSUN
// User sudah terdaftar & Event sedang berlangsung & user harus konfirmasi // User sudah terdaftar & Event sedang berlangsung & user harus konfirmasi
const UserParticipan_And_DuringEvent = ({ const UserParticipan_And_DuringEvent = ({

View File

@@ -7,6 +7,7 @@ import {
Spacing, Spacing,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection"; import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
@@ -49,9 +50,13 @@ export default function EventDetailContribution() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail kontribusi`, header: () => (
headerLeft: () => <LeftButtonCustom />, <AppHeader
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />, title="Detail kontribusi"
left={<LeftButtonCustom />}
right={<DotButton onPress={() => setOpenDrawer(true)} />}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -6,6 +6,7 @@ import {
ViewWrapper, ViewWrapper,
Spacing, Spacing,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection"; import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
@@ -44,9 +45,13 @@ export default function EventDetailHistory() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail riwayat`, header: () => (
headerLeft: () => <LeftButtonCustom />, <AppHeader
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />, title="Detail riwayat"
left={<LeftButtonCustom />}
right={<DotButton onPress={() => setOpenDrawer(true)} />}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -8,6 +8,7 @@ import {
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom"; import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
@@ -156,9 +157,13 @@ export default function EventDetailPublish() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Event Publish`, header: () => (
headerLeft: () => <BackButton onPress={() => router.back()} />, <AppHeader
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />, title="Event Publish"
left={<BackButton onPress={() => router.back()} />}
right={<DotButton onPress={() => setOpenDrawer(true)} />}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { BasicWrapper, StackCustom, ViewWrapper } from "@/components"; import { BasicWrapper, StackCustom, ViewWrapper } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom"; import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
@@ -117,28 +118,36 @@ export default function Application() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `HIPMI`, header: () => (
headerLeft: () => <AppHeader
data ? ( title="HIPMI"
<Ionicons showBack={false}
name="search" left={
size={20} data ? (
color={MainColor.yellow} <Ionicons
onPress={() => { name="search"
router.push("/user-search"); size={20}
}} color={MainColor.yellow}
/> onPress={() => {
) : ( router.push("/user-search");
<CustomSkeleton height={30} width={30} radius={100} /> }}
), />
headerRight: () => ) : (
data ? ( <CustomSkeleton height={30} width={30} radius={100} />
<HeaderBell /> )
) : ( }
<CustomSkeleton height={30} width={30} radius={100} /> right={
), data ? (
<HeaderBell />
) : (
<CustomSkeleton height={30} width={30} radius={100} />
)
}
/>
),
}} }}
/> />
<ViewWrapper <ViewWrapper
refreshControl={ refreshControl={
<RefreshControl <RefreshControl

View File

@@ -10,6 +10,7 @@ import {
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconDocument, IconEdit, IconNews } from "@/components/_Icon"; import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
@@ -30,13 +31,13 @@ export default function InvestmentDetailHolding() {
const [openDrawerDraft, setOpenDrawerDraft] = useState(false); const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
const [openDrawerPublish, setOpenDrawerPublish] = useState(false); const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
const [data, setData] = useState<any>(null); const [data, setData] = useState<any>(null);
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
onLoadData(); onLoadData();
}, [id, status]) }, [id, status])
); );
const onLoadData = async () => { const onLoadData = async () => {
try { try {
const response = await apiInvestmentGetInvoice({ const response = await apiInvestmentGetInvoice({
@@ -44,7 +45,7 @@ export default function InvestmentDetailHolding() {
authorId: user?.id, authorId: user?.id,
category: "invoice", category: "invoice",
}); });
console.log("[DATA]", JSON.stringify(response.data, null, 2)); console.log("[DATA]", JSON.stringify(response.data, null, 2));
setData(response.data); setData(response.data);
} catch (error) { } catch (error) {
@@ -76,14 +77,19 @@ export default function InvestmentDetailHolding() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail ${_.startCase(status as string)}`, header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => title={`Detail ${_.startCase(status as string)}`}
status === "draft" ? ( left={<BackButton />}
<DotButton onPress={() => setOpenDrawerDraft(true)} /> right={
) : status === "publish" ? ( status === "draft" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} /> <DotButton onPress={() => setOpenDrawerDraft(true)} />
) : null, ) : status === "publish" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} />
) : null
}
/>
),
}} }}
/> />

View File

@@ -11,6 +11,7 @@ import {
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconTrash } from "@/components/_Icon/IconTrash"; import { IconTrash } from "@/components/_Icon/IconTrash";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { import {
@@ -56,12 +57,17 @@ export default function InvestmentNews() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Detail Berita", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => title="Detail Berita"
user?.id === data?.authorId && ( left={<BackButton />}
<DotButton onPress={() => setOpenDrawer(true)} /> right={
), user?.id === data?.authorId && (
<DotButton onPress={() => setOpenDrawer(true)} />
)
}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -6,6 +6,7 @@ import {
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconDocument, IconEdit, IconNews } from "@/components/_Icon"; import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
@@ -106,14 +107,19 @@ export default function InvestmentDetailStatus() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail ${_.startCase(status as string)}`, header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => title={`Detail ${_.startCase(status as string)}`}
status === "draft" ? ( left={<BackButton />}
<DotButton onPress={() => setOpenDrawerDraft(true)} /> right={
) : status === "publish" ? ( status === "draft" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} /> <DotButton onPress={() => setOpenDrawerDraft(true)} />
) : null, ) : status === "publish" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} />
) : null
}
/>
),
}} }}
/> />

View File

@@ -6,6 +6,7 @@ import {
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconDocument, IconEdit, IconNews } from "@/components/_Icon"; import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
@@ -105,14 +106,19 @@ export default function InvestmentDetail() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail ${_.startCase(status as string)}`, header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => title={`Detail ${_.startCase(status as string)}`}
status === "draft" ? ( left={<BackButton />}
<DotButton onPress={() => setOpenDrawerDraft(true)} /> right={
) : status === "publish" ? ( status === "draft" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} /> <DotButton onPress={() => setOpenDrawerDraft(true)} />
) : null, ) : status === "publish" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} />
) : null
}
/>
),
}} }}
/> />

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { BackButton } from "@/components"; import { BackButton } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconHome, IconStatus } from "@/components/_Icon"; import { IconHome, IconStatus } from "@/components/_Icon";
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification"; import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
import { TabsStyles } from "@/styles/tabs-styles"; import { TabsStyles } from "@/styles/tabs-styles";
@@ -7,31 +8,30 @@ import { Ionicons } from "@expo/vector-icons";
import { import {
router, router,
Tabs, Tabs,
useLocalSearchParams, useLocalSearchParams
useNavigation
} from "expo-router"; } from "expo-router";
import { useLayoutEffect } from "react";
export default function JobTabsLayout() { export default function JobTabsLayout() {
const navigation = useNavigation();
const { from, category } = useLocalSearchParams<{ const { from, category } = useLocalSearchParams<{
from?: string; from?: string;
category?: string; category?: string;
}>(); }>();
// Atur header secara dinamis
useLayoutEffect(() => {
navigation.setOptions({
headerLeft: () => (
<BackButtonFromNotification from={from as string} category={category as string} />
),
});
}, [from, router, navigation]);
return ( return (
<> <>
<Tabs screenOptions={TabsStyles}> <Tabs
screenOptions={{
...TabsStyles,
header: () => (
<AppHeader
title="Job Vacancy"
left={
<BackButtonFromNotification from={from as string} category={category as string} />
}
/>
),
}}
>
<Tabs.Screen <Tabs.Screen
name="index" name="index"
options={{ options={{

View File

@@ -9,6 +9,7 @@ import {
StackCustom, StackCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconEdit } from "@/components/_Icon"; import { IconEdit } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import ReportBox from "@/components/Box/ReportBox"; import ReportBox from "@/components/Box/ReportBox";
@@ -58,12 +59,17 @@ export default function JobDetailStatus() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail`, header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => title="Detail"
status === "draft" ? ( left={<BackButton />}
<DotButton onPress={() => setOpenDrawer(true)} /> right={
) : null, status === "draft" ? (
<DotButton onPress={() => setOpenDrawer(true)} />
) : null
}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -1,365 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ import { Admin_ScreenPortofolioCreate } from "@/screens/Portofolio/ScreenPortofolioCreate";
import {
ActionIcon,
AvatarComp,
BaseBox,
ButtonCenteredOnly,
CenterCustom,
Grid,
InformationBox,
NewWrapper,
SelectCustom,
Spacing,
StackCustom,
TextAreaCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { IconPlus } from "@/components/_Icon";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
import DUMMY_IMAGE from "@/constants/dummy-image-value";
import Portofolio_ButtonCreate from "@/screens/Portofolio/ButtonCreatePortofolio";
import {
apiMasterBidangBisnis,
apiMasterSubBidangBisnis,
} from "@/service/api-client/api-master";
import {
IMasterBidangBisnis,
IMasterSubBidangBisnis,
} from "@/types/Type-Master";
import pickImage from "@/utils/pickImage";
import { Ionicons } from "@expo/vector-icons";
import { Image } from "expo-image";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useEffect, useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import PhoneInput, { ICountry } from "react-native-international-phone-number";
import { Avatar } from "react-native-paper";
export default function PortofolioCreate() { export default function PortofolioCreate() {
const { id } = useLocalSearchParams(); return <Admin_ScreenPortofolioCreate />;
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
const [inputValue, setInputValue] = useState<string>("");
const [data, setData] = useState({
namaBisnis: "",
masterBidangBisnisId: "",
alamatKantor: "",
tlpn: "",
deskripsi: "",
});
const [imageUri, setImageUri] = useState<string | null>(null);
const [bidangBisnis, setBidangBisnis] = useState<IMasterBidangBisnis[]>([]);
const [subBidangBisnis, setSubBidangBisnis] = useState<
IMasterSubBidangBisnis[]
>([]);
const [selectedSubBidang, setSelectedSubBidang] = useState<string[]>([]);
const [listSubBidangSelected, setListSubBidangSelected] = useState([
{
id: "",
},
]);
const [dataMedsos, setDataMedsos] = useState({
facebook: "",
twitter: "",
instagram: "",
youtube: "",
tiktok: "",
});
const [isLoadingCreate, setIsLoadingCreate] = useState(false);
function handleInputValue(phoneNumber: string) {
setInputValue(phoneNumber);
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, "");
const realNumber = callingCode + fixNumber;
setData({ ...data, tlpn: realNumber });
}
function handleSelectedCountry(country: ICountry) {
setSelectedCountry(country);
}
useFocusEffect(
useCallback(() => {
onLoadMaster();
onLoadMasterSubBidangBisnis();
}, [])
);
const onLoadMaster = async () => {
try {
const response = await apiMasterBidangBisnis();
setBidangBisnis(response.data);
} catch (error) {
setBidangBisnis([]);
console.log("Error onLoadMasterBidangBisnis", error);
}
};
const onLoadMasterSubBidangBisnis = async () => {
try {
const response = await apiMasterSubBidangBisnis({});
setSubBidangBisnis(response.data);
} catch (error) {
setSubBidangBisnis([]);
console.log("Error onLoadMasterBidangBisnis", error);
}
};
const handlerSelectedSubBidang = ({ id }: { id: string }) => {
const selectedList = subBidangBisnis?.filter(
(item) => (item?.masterBidangBisnisId as any) === id
);
setSelectedSubBidang(selectedList as any[]);
};
return (
<NewWrapper
footerComponent={
<Portofolio_ButtonCreate
id={id as string}
data={data}
dataMedsos={dataMedsos}
imageUri={imageUri}
subBidangSelected={listSubBidangSelected}
isLoadingCreate={isLoadingCreate}
setIsLoadingCreate={setIsLoadingCreate}
/>
}
>
{/* <TextCustom>Portofolio Create {id}</TextCustom> */}
<StackCustom gap={"xs"}>
<InformationBox text="Lengkapi data bisnis anda." />
<TextInputCustom
required
label="Nama Bisnis"
placeholder="Masukkan nama bisnis"
onChangeText={(value: any) => setData({ ...data, namaBisnis: value })}
/>
<SelectCustom
label="Bidang Usaha"
required
data={bidangBisnis.map((item) => ({
label: item.name,
value: item.id,
}))}
value={data.masterBidangBisnisId}
onChange={(value) => {
const isSameBidang = data.masterBidangBisnisId === value;
if (!isSameBidang) {
setListSubBidangSelected([{ id: "" }]);
}
setData({ ...(data as any), masterBidangBisnisId: value });
handlerSelectedSubBidang({ id: value as string });
}}
/>
{listSubBidangSelected.map((item, index) => (
<SelectCustom
key={index}
disabled={data.masterBidangBisnisId === ""}
label="Sub Bidang Usaha"
required
data={_.map(selectedSubBidang as any)
.filter((option: any) => {
const selectedValues = listSubBidangSelected.map((s) => s.id);
return (
option.id === item.id || // biarkan tetap muncul kalau ini valuenya sendiri
!selectedValues.includes(option.id)
);
})
.map((e: any) => ({
value: e.id,
label: e.name,
}))}
value={item.id || null}
onChange={(value) => {
const list = _.clone(listSubBidangSelected);
list[index].id = value as any;
setListSubBidangSelected(list);
}}
/>
))}
<CenterCustom>
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
<ActionIcon
disabled={
selectedSubBidang.length === listSubBidangSelected.length
}
onPress={() => {
setListSubBidangSelected([
...listSubBidangSelected,
{ id: "" },
]);
}}
icon={
<Ionicons
name="add-circle-outline"
size={ICON_SIZE_XLARGE}
color={MainColor.black}
/>
}
size="xl"
/>
<ActionIcon
disabled={listSubBidangSelected.length <= 1}
onPress={() => {
const list = _.clone(listSubBidangSelected);
list.pop();
setListSubBidangSelected(list);
}}
icon={
<Ionicons
name="remove-circle-outline"
size={ICON_SIZE_XLARGE}
color={MainColor.black}
/>
}
size="xl"
/>
</View>
</CenterCustom>
<Spacing />
{/* <SelectCustom
label="Bidang Usaha"
required
data={bidangBisnis.map((item) => ({
label: item.name,
value: item.id,
}))}
value={null}
onChange={(value) => {
setData({ ...(data as any), masterBidangBisnisId: value });
handlerSelectedSubBidang({ id: value as string });
}}
/> */}
{/* <ButtonCenteredOnly
onPress={() => {
setListSubBidangSelected([...listSubBidangSelected, { id: "" }]);
}}
>
Tambah Pilihan
</ButtonCenteredOnly>
<Spacing /> */}
{/* <TextCustom>{JSON.stringify(bidangBisnis, null, 2)}</TextCustom> */}
<View>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
Nomor Telepon
</TextCustom>
<Text style={{ color: "red" }}> *</Text>
</View>
<Spacing height={5} />
<PhoneInput
value={inputValue}
onChangePhoneNumber={handleInputValue}
selectedCountry={selectedCountry}
onChangeSelectedCountry={handleSelectedCountry}
defaultCountry="ID"
placeholder="xxx-xxx-xxx"
/>
</View>
<Spacing />
<TextInputCustom
required
label="Alamat Bisnis"
placeholder="Masukkan alamat bisnis"
onChangeText={(value: any) =>
setData({ ...data, alamatKantor: value })
}
/>
<TextAreaCustom
label="Deskripsi Bisnis"
placeholder="Masukkan deskripsi bisnis"
value={data.deskripsi}
onChangeText={(value: any) => setData({ ...data, deskripsi: value })}
autosize
minRows={2}
maxRows={5}
required
showCount
maxLength={1000}
/>
<Spacing />
{/* Logo */}
<InformationBox text="Upload logo bisnis anda untuk di tampilaka pada portofolio." />
<CenterCustom>
<Avatar.Image
source={imageUri ? { uri: imageUri } : DUMMY_IMAGE.dummy_image}
size={200}
/>
</CenterCustom>
<Spacing />
<ButtonCenteredOnly
icon="upload"
onPress={() => {
pickImage({
setImageUri,
});
}}
>
Upload
</ButtonCenteredOnly>
<Spacing height={40} />
{/* Social Media */}
<InformationBox text="Isi hanya pada sosial media yang anda miliki." />
<TextInputCustom
label="Tiktok"
placeholder="Masukkan username tiktok"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, tiktok: value })
}
/>
<TextInputCustom
label="Facebook"
placeholder="Masukkan username facebook"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, facebook: value })
}
/>
<TextInputCustom
label="Instagram"
placeholder="Masukkan username instagram"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, instagram: value })
}
/>
<TextInputCustom
label="Twitter"
placeholder="Masukkan username twitter"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, twitter: value })
}
/>
<TextInputCustom
label="Youtube"
placeholder="Masukkan username youtube"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, youtube: value })
}
/>
{/* <Spacing /> */}
</StackCustom>
</NewWrapper>
);
} }

View File

@@ -8,6 +8,7 @@ import {
StackCustom, StackCustom,
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import GridTwoView from "@/components/_ShareComponent/GridTwoView"; import GridTwoView from "@/components/_ShareComponent/GridTwoView";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom"; import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
@@ -72,20 +73,23 @@ export default function Portofolio() {
{/* Header */} {/* Header */}
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Portofolio", header: () => (
headerLeft: () => <LeftButtonCustom />, <AppHeader
headerRight: () => title="Portofolio"
data?.Profile?.id !== profileId ? null : ( left={<LeftButtonCustom />}
<TouchableOpacity onPress={openDrawer}> right={
<Ionicons data?.Profile?.id !== profileId ? null : (
name="ellipsis-vertical" <TouchableOpacity onPress={openDrawer}>
size={20} <Ionicons
color={MainColor.yellow} name="ellipsis-vertical"
/> size={20}
</TouchableOpacity> color={MainColor.yellow}
), />
headerStyle: GStyles.headerStyle, </TouchableOpacity>
headerTitleStyle: GStyles.headerTitleStyle, )
}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -1,5 +1,5 @@
import AppHeader from "@/components/_ShareComponent/AppHeader";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import { HeaderStyles } from "@/styles/header-styles";
import { Stack } from "expo-router"; import { Stack } from "expo-router";
export default function PortofolioLayout() { export default function PortofolioLayout() {
@@ -7,8 +7,9 @@ export default function PortofolioLayout() {
<> <>
<Stack <Stack
screenOptions={{ screenOptions={{
...HeaderStyles, header: () => (
headerLeft: () => <LeftButtonCustom />, <AppHeader title="Portofolio" left={<LeftButtonCustom />} />
),
}} }}
> >
{/* <Stack.Screen name="[id]/index" options={{ title: "Portofolio" }} /> */} {/* <Stack.Screen name="[id]/index" options={{ title: "Portofolio" }} /> */}

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { NewWrapper, StackCustom } from "@/components"; import { NewWrapper, StackCustom } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom"; import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import DrawerCustom from "@/components/Drawer/DrawerCustom"; import DrawerCustom from "@/components/Drawer/DrawerCustom";
@@ -101,18 +102,20 @@ export default function Profile() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Profile`, header: () => (
headerLeft: () => <LeftButtonCustom />, <AppHeader
headerRight: () => ( title="Profile"
<ButtonnDot left={<LeftButtonCustom />}
id={id as string} right={
openDrawer={openDrawer} <ButtonnDot
isUserCheck={isUserCheck()} id={id as string}
logout={logout} openDrawer={openDrawer}
isUserCheck={isUserCheck()}
logout={logout}
/>
}
/> />
), ),
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
}} }}
/> />
{/* Main View */} {/* Main View */}

View File

@@ -1,47 +1,39 @@
import { BackButton } from "@/components"; import { BackButton } from "@/components";
import { GStyles } from "@/styles/global-styles"; import AppHeader from "@/components/_ShareComponent/AppHeader";
import { Stack } from "expo-router"; import { Stack } from "expo-router";
export default function ProfileLayout() { export default function ProfileLayout() {
return ( return (
<> <>
<Stack <Stack>
screenOptions={{
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
headerTitleAlign: "center",
headerBackButtonDisplayMode: "minimal",
}}
>
{/* <Stack.Screen name="[id]/index" options={{ headerShown: false }} /> */} {/* <Stack.Screen name="[id]/index" options={{ headerShown: false }} /> */}
<Stack.Screen <Stack.Screen
name="[id]/edit" name="[id]/edit"
options={{ title: "Edit Profile", headerLeft: () => <BackButton /> }} options={{ header: () => <AppHeader title="Edit Profile" /> }}
/> />
<Stack.Screen <Stack.Screen
name="[id]/update-photo" name="[id]/update-photo"
options={{ title: "Update Foto", headerLeft: () => <BackButton /> }} options={{ header: () => <AppHeader title="Update Foto" /> }}
/> />
<Stack.Screen <Stack.Screen
name="[id]/update-background" name="[id]/update-background"
options={{ options={{
title: "Update Latar Belakang", header: () => <AppHeader title="Update Latar Belakang" />,
headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen <Stack.Screen
name="create" name="create"
options={{ title: "Buat Profile", headerBackVisible: false }} options={{ headerBackVisible: false }}
/> />
<Stack.Screen <Stack.Screen
name="[id]/blocked-list" name="[id]/blocked-list"
options={{ title: "Daftar Blokir", headerLeft: () => <BackButton /> }} options={{ header: () => <AppHeader title="Daftar Blokir" /> }}
/> />
<Stack.Screen <Stack.Screen
name="[id]/detail-blocked" name="[id]/detail-blocked"
options={{ title: "Detail Blokir", headerLeft: () => <BackButton /> }} options={{ header: () => <AppHeader title="Detail Blokir" /> }}
/> />
</Stack> </Stack>
</> </>

View File

@@ -4,36 +4,34 @@ import {
IconHome, IconHome,
IconStatus, IconStatus,
} from "@/components/_Icon"; } from "@/components/_Icon";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification"; import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
import { TabsStyles } from "@/styles/tabs-styles"; import { TabsStyles } from "@/styles/tabs-styles";
import { Tabs, useLocalSearchParams, useNavigation, router } from "expo-router"; import { router, Tabs, useLocalSearchParams } from "expo-router";
import { useLayoutEffect } from "react";
export default function VotingTabsLayout() { export default function VotingTabsLayout() {
const navigation = useNavigation();
const { from, category } = useLocalSearchParams<{ const { from, category } = useLocalSearchParams<{
from?: string; from?: string;
category?: string; category?: string;
}>(); }>();
console.log("from", from);
console.log("category", category);
// Atur header secara dinamis
useLayoutEffect(() => {
navigation.setOptions({
headerLeft: () => (
<BackButtonFromNotification
from={from as string}
category={category as string}
/>
),
});
}, [from, router, navigation]);
return ( return (
<Tabs screenOptions={TabsStyles}> <Tabs
screenOptions={{
...TabsStyles,
header: () => (
<AppHeader
title="Voting"
left={
<BackButtonFromNotification
from={from as string}
category={category as string}
/>
}
/>
),
}}
>
<Tabs.Screen <Tabs.Screen
name="index" name="index"
options={{ options={{

View File

@@ -12,6 +12,7 @@ import {
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon"; import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import ReportBox from "@/components/Box/ReportBox"; import ReportBox from "@/components/Box/ReportBox";
@@ -103,14 +104,19 @@ export default function VotingDetailStatus() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail`, header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => title="Detail"
status === "draft" ? ( left={<BackButton />}
<DotButton onPress={() => setOpenDrawerDraft(true)} /> right={
) : status === "publish" ? ( status === "draft" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} /> <DotButton onPress={() => setOpenDrawerDraft(true)} />
) : null, ) : status === "publish" ? (
<DotButton onPress={() => setOpenDrawerPublish(true)} />
) : null
}
/>
),
}} }}
/> />
<ViewWrapper> <ViewWrapper>

View File

@@ -9,6 +9,7 @@ import {
Spacing, Spacing,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconContribution } from "@/components/_Icon"; import { IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
@@ -81,10 +82,14 @@ export default function VotingDetailContribution() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Detail Kontribusi", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Detail Kontribusi"
<DotButton onPress={() => setOpenDrawerPublish(true)} /> left={<BackButton />}
right={
<DotButton onPress={() => setOpenDrawerPublish(true)} />
}
/>
), ),
}} }}
/> />

View File

@@ -9,6 +9,7 @@ import {
Spacing, Spacing,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconContribution } from "@/components/_Icon"; import { IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
@@ -82,10 +83,14 @@ export default function VotingDetailHistory() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Riwayat Voting", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Riwayat Voting"
<DotButton onPress={() => setOpenDrawerPublish(true)} /> left={<BackButton />}
right={
<DotButton onPress={() => setOpenDrawerPublish(true)} />
}
/>
), ),
}} }}
/> />

View File

@@ -11,6 +11,7 @@ import {
StackCustom, StackCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconArchive, IconContribution } from "@/components/_Icon"; import { IconArchive, IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom"; import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
@@ -142,10 +143,14 @@ export default function VotingDetail() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail Voting`, header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Detail Voting"
<DotButton onPress={() => setOpenDrawerPublish(true)} /> left={<BackButton />}
right={
<DotButton onPress={() => setOpenDrawerPublish(true)} />
}
/>
), ),
}} }}
/> />

View File

@@ -1,8 +1,8 @@
import { BackButton } from "@/components"; import { BackButton } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import BackgroundNotificationHandler from "@/components/Notification/BackgroundNotificationHandler"; import BackgroundNotificationHandler from "@/components/Notification/BackgroundNotificationHandler";
import NotificationInitializer from "@/components/Notification/NotificationInitializer"; import NotificationInitializer from "@/components/Notification/NotificationInitializer";
import { NotificationProvider } from "@/hooks/use-notification-store"; import { NotificationProvider } from "@/hooks/use-notification-store";
import { HeaderStyles } from "@/styles/header-styles";
import { Stack } from "expo-router"; import { Stack } from "expo-router";
export default function ApplicationLayout() { export default function ApplicationLayout() {
@@ -20,7 +20,7 @@ export default function ApplicationLayout() {
function ApplicationStack() { function ApplicationStack() {
return ( return (
<> <>
<Stack screenOptions={HeaderStyles}> <Stack>
<Stack.Screen name="(user)" options={{ headerShown: false }} /> <Stack.Screen name="(user)" options={{ headerShown: false }} />
<Stack.Screen name="admin" options={{ headerShown: false }} /> <Stack.Screen name="admin" options={{ headerShown: false }} />
@@ -28,8 +28,7 @@ function ApplicationStack() {
<Stack.Screen <Stack.Screen
name="(image)/take-picture/[id]/index" name="(image)/take-picture/[id]/index"
options={{ options={{
title: "Ambil Gambar", header: () => <AppHeader title="Ambil Gambar" />,
headerLeft: () => <BackButton />,
}} }}
/> />
@@ -37,8 +36,7 @@ function ApplicationStack() {
<Stack.Screen <Stack.Screen
name="(image)/preview-image/[id]/index" name="(image)/preview-image/[id]/index"
options={{ options={{
title: "Preview Gambar", header: () => <AppHeader title="Preview Gambar" />,
headerLeft: () => <BackButton />,
}} }}
/> />
</Stack> </Stack>

View File

@@ -6,6 +6,7 @@ import {
StackCustom, StackCustom,
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import DrawerAdmin from "@/components/Drawer/DrawerAdmin"; import DrawerAdmin from "@/components/Drawer/DrawerAdmin";
import NavbarMenu from "@/components/Drawer/NavbarMenu"; import NavbarMenu from "@/components/Drawer/NavbarMenu";
import NavbarMenu_V2 from "@/components/Drawer/NavbarMenu_V2"; import NavbarMenu_V2 from "@/components/Drawer/NavbarMenu_V2";
@@ -17,6 +18,7 @@ import {
ICON_SIZE_XLARGE, ICON_SIZE_XLARGE,
} from "@/constants/constans-value"; } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { useNotificationStore } from "@/hooks/use-notification-store";
import AdminNotificationBell from "@/screens/Admin/AdminNotificationBell"; import AdminNotificationBell from "@/screens/Admin/AdminNotificationBell";
import { import {
adminListMenu, adminListMenu,
@@ -34,12 +36,28 @@ import { useState } from "react";
export default function AdminLayout() { export default function AdminLayout() {
const [openDrawerNavbar, setOpenDrawerNavbar] = useState(false); const [openDrawerNavbar, setOpenDrawerNavbar] = useState(false);
const [openDrawerUser, setOpenDrawerUser] = useState(false); const [openDrawerUser, setOpenDrawerUser] = useState(false);
// const [user, setUser] = useState(null);
const { logout, user } = useAuth(); const { logout, user } = useAuth();
console.log("[USER LAYOUT]", JSON.stringify(user, null, 2)); console.log("[USER LAYOUT]", JSON.stringify(user, null, 2));
const headerLeft = () => (
<Ionicons
name="menu"
size={ICON_SIZE_XLARGE}
color={MainColor.white}
onPress={() => setOpenDrawerNavbar(true)}
/>
);
const headerRight = () => (
<FontAwesome6
name="circle-user"
size={ICON_SIZE_MEDIUM}
color={MainColor.white}
onPress={() => setOpenDrawerUser(true)}
/>
);
return ( return (
<> <>
<Stack <Stack
@@ -51,20 +69,33 @@ export default function AdminLayout() {
contentStyle: { contentStyle: {
borderBottomColor: AccentColor.blue, borderBottomColor: AccentColor.blue,
}, },
headerLeft: () => (
<Ionicons // headerLeft: () => (
name="menu" // <Ionicons
size={ICON_SIZE_XLARGE} // name="menu"
color={MainColor.white} // size={ICON_SIZE_XLARGE}
onPress={() => setOpenDrawerNavbar(true)} // color={MainColor.white}
/> // onPress={() => setOpenDrawerNavbar(true)}
), // />
headerRight: () => ( // ),
<FontAwesome6 // headerRight: () => (
name="circle-user" // <FontAwesome6
size={ICON_SIZE_MEDIUM} // name="circle-user"
color={MainColor.white} // size={ICON_SIZE_MEDIUM}
onPress={() => setOpenDrawerUser(true)} // color={MainColor.white}
// onPress={() => setOpenDrawerUser(true)}
// />
// ),
header: () => (
<AppHeader
title="HIPMI DASHBOARD"
showBack={false}
left={headerLeft()}
right={headerRight()}
/> />
), ),
}} }}

View File

@@ -1,4 +1,5 @@
import { BackButton, StackCustom, TextCustom, ViewWrapper } from "@/components"; import { BackButton, StackCustom, TextCustom, ViewWrapper } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { router, Stack } from "expo-router"; import { router, Stack } from "expo-router";
export default function NotFoundScreen() { export default function NotFoundScreen() {
@@ -15,7 +16,7 @@ export default function NotFoundScreen() {
return ( return (
<> <>
<Stack.Screen <Stack.Screen
options={{ headerShown: true, title: "", headerLeft: () => <BackButton onPress={() => handleBack()} /> }} options={{ header: () => <AppHeader title="" left={<BackButton onPress={() => handleBack()} />} /> }}
/> />
<ViewWrapper> <ViewWrapper>
<StackCustom <StackCustom

View File

@@ -12,14 +12,15 @@ export default function BackButtonFromNotification({
return ( return (
<> <>
<BackButton <BackButton
onPress={() => { onPress={() => {
if (from === "notifications") { if (from === "notifications") {
router.replace(`/notifications?category=${category}`); router.push(`/notifications?category=${category}`);
} else { } else {
if (from) { if (from) {
router.replace(`/${from}` as any); router.back();
} else { } else {
router.navigate("/home"); router.back();
} }
} }
}} }}

View File

@@ -0,0 +1,114 @@
import { MainColor } from "@/constants/color-palet";
import { Platform, StyleSheet, Text, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { BackButton } from "..";
type Props = {
title: string;
right?: React.ReactNode;
showBack?: boolean;
onPressLeft?: () => void;
left?: React.ReactNode;
};
export default function AppHeader({
title,
right,
showBack = true,
onPressLeft,
left,
}: Props) {
const insets = useSafeAreaInsets();
// iOS 16+ detection (Dynamic Island) - insets.top > 47 indicates Dynamic Island
const isIOS26Plus =
Platform.OS === "ios" && insets.top > 47;
// Dynamic padding berdasarkan platform dan iOS version
const paddingTop =
Platform.OS === "ios"
? isIOS26Plus
? insets.top - 10
: insets.top
: 10;
const paddingBottom = Platform.OS === "ios" ? 8 : 13;
return (
<View
style={[
{
backgroundColor: MainColor.darkblue,
paddingTop,
paddingBottom,
},
]}
pointerEvents="box-none"
>
{/* Header Container dengan absolute positioning untuk title center */}
<View style={styles.headerApp} pointerEvents="box-none">
{/* Left Section - Absolute Left */}
<View style={styles.headerLeft}>
{showBack ? (
<BackButton onPress={onPressLeft} />
) : left ? (
left
) : (
<View style={styles.placeholder} />
)}
</View>
{/* Title - Absolute Center */}
<View style={styles.headerCenter}>
<Text
style={styles.headerTitle}
numberOfLines={1}
ellipsizeMode="tail"
>
{title ? title.charAt(0).toUpperCase() + title.slice(1) : ""}
</Text>
</View>
{/* Right Section - Absolute Right */}
<View style={styles.headerRight}>
{right}
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
headerApp: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
paddingHorizontal: 16,
height: 44, // Fixed height untuk consistency
},
headerLeft: {
position: "absolute",
left: 16,
zIndex: 1,
},
headerCenter: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
headerRight: {
position: "absolute",
right: 16,
zIndex: 1,
},
placeholder: {
width: 40,
height: 40,
},
headerTitle: {
color: MainColor.yellow,
fontSize: 18,
fontWeight: "600",
textAlign: "center",
},
});

View File

@@ -22,7 +22,7 @@ type AuthContextType = {
isAdmin: boolean; isAdmin: boolean;
isUserActive: boolean; isUserActive: boolean;
loginWithNomor: (nomor: string) => Promise<boolean>; loginWithNomor: (nomor: string) => Promise<boolean>;
validateOtp: (nomor: string) => Promise<any>; validateOtp: (nomor: string, code: string) => Promise<any>;
logout: () => Promise<void>; logout: () => Promise<void>;
registerUser: (userData: { registerUser: (userData: {
username: string; username: string;
@@ -97,10 +97,10 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
}; };
// --- 2. Validasi OTP & cek user --- // --- 2. Validasi OTP & cek user ---
const validateOtp = async (nomor: string) => { const validateOtp = async (nomor: string, code: string) => {
try { try {
setIsLoading(true); setIsLoading(true);
const response = await apiValidationCode({ nomor: nomor }); const response = await apiValidationCode({ nomor: nomor, code: code });
const { token } = response; const { token } = response;
console.log("[RESPONSE VALIDASI OTP]", JSON.stringify(response, null, 2)); console.log("[RESPONSE VALIDASI OTP]", JSON.stringify(response, null, 2));

253
docs/QR_CODE_TESTING.md Normal file
View File

@@ -0,0 +1,253 @@
# QR Code Testing Guide - HIPMI Mobile
## 📋 Overview
Dokumentasi ini menjelaskan cara testing QR Code untuk Universal Links (iOS) dan App Links (Android) pada fitur Event Confirmation.
## 🔧 Update Terbaru
File `screens/Admin/Event/EventDetailQRCode.tsx` telah diupdate dengan fitur:
- **Toggle Button**: Switch antara HTTPS link dan Custom Scheme link
- **HTTPS Link**: Untuk testing Universal Links/App Links dengan domain staging
- **Custom Scheme**: Untuk testing langsung tanpa domain verification
## 🎯 Cara Testing QR Code
### Opsi 1: HTTPS Link (Recommended untuk Production)
**Gunakan tombol "HTTPS"** di component QR Code.
**Link yang di-generate:**
```
https://cld-dkr-staging-hipmi.wibudev.com/event/{id}/confirmation?userId={userId}
```
**Cara kerja:**
1. User scan QR code dengan kamera
2. Safari/Chrome terbuka dengan URL HTTPS
3. iOS/Android mendeteksi domain terverifikasi
4. App terbuka otomatis dan menuju halaman confirmation
**Prerequisites:**
- ✅ File `apple-app-site-association` harus accessible di Next.js server
- ✅ File `assetlinks.json` harus accessible di Next.js server
- ✅ Domain harus terverifikasi di app.config.js
- ✅ App harus di-build ulang setelah perubahan domain
**Testing Steps:**
```bash
# 1. Pastikan .well-known files accessible
curl https://cld-dkr-staging-hipmi.wibudev.com/.well-known/apple-app-site-association
curl https://cld-dkr-staging-hipmi.wibudev.com/.well-known/assetlinks.json
# 2. Rebuild app
bunx expo prebuild --clean
# 3. Run di physical device (bukan simulator)
bun run android # untuk Android
bun run ios # untuk iOS
```
### Opsi 2: Custom Scheme Link (Untuk Development/Testing Cepat)
**Gunakan tombol "Custom Scheme"** di component QR Code.
**Link yang di-generate:**
```
hipmimobile://event/{id}/confirmation?userId={userId}
```
**Cara kerja:**
1. User scan QR code dengan kamera
2. iOS: Pilih "Open in HIPMI Badung Connect"
3. Android: Langsung buka app
4. App terbuka dan menuju halaman confirmation
**Keuntungan:**
- ✅ Tidak butuh domain verification
- ✅ Bisa testing langsung tanpa rebuild
- ✅ Cocok untuk development
**Kekurangan:**
- ❌ Tidak bisa dibuka dari web browser
- ❌ Tidak support universal linking dari website lain
## 📱 Testing Checklist
### iOS (Universal Links)
- [ ] File `apple-app-site-association` valid dan accessible
- [ ] Domain terdaftar di `app.config.js``ios.associatedDomains`
- [ ] Bundle ID match dengan konfigurasi
- [ ] Team ID benar di apple-app-site-association
- [ ] Test dengan **physical device** (simulator tidak support)
- [ ] Test dengan **Safari** (bukan Chrome)
- [ ] Long press link → ada opsi "Open"
**Debug iOS:**
```bash
# Cek apple-app-site-association
curl -I https://cld-dkr-staging-hipmi.wibudev.com/.well-known/apple-app-site-association
# Harus return:
# Content-Type: application/json
# HTTP/2 200
```
### Android (App Links)
- [ ] File `assetlinks.json` valid dan accessible
- [ ] SHA256 fingerprint benar
- [ ] Package name match
- [ ] Intent filters terdaftar di app.config.js
- [ ] Test dengan **physical device**
- [ ] Test dengan **Chrome**
**Debug Android:**
```bash
# Dapatkan SHA256 fingerprint
cd android
./gradlew signingReport
# Cek assetlinks.json
curl https://cld-dkr-staging-hipmi.wibudev.com/.well-known/assetlinks.json
```
## 🐛 Troubleshooting
### Problem: QR Scan Terbuka di Safari, Tidak Balik ke App
**Penyebab:**
- Domain belum terverifikasi untuk Universal Links/App Links
- File `.well-known` tidak accessible atau invalid
- App belum di-rebuild setelah perubahan domain
**Solusi:**
1. Pastikan file `.well-known` accessible:
```bash
curl https://cld-dkr-staging-hipmi.wibudev.com/.well-known/apple-app-site-association
```
2. Rebuild app:
```bash
bunx expo prebuild --clean
bun run android # atau bun run ios
```
3. Gunakan **Custom Scheme** untuk testing cepat
### Problem: Link Tidak Membuka App Sama Sekali
**Cek:**
1. App sudah terinstall di device
2. Link format benar (hipmimobile:// atau https://)
3. Route handler sudah ada di app folder
**Test manual:**
```bash
# iOS Simulator
xcrun simctl openurl booted "hipmimobile://event/123/confirmation?userId=456"
# Android Emulator
adb shell am start -W -a android.intent.action.VIEW \
-d "hipmimobile://event/123/confirmation?userId=456" \
com.bip.hipmimobileapp
```
### Problem: "Cannot GET /event/..." di Next.js
**Penyebab:**
Route `/event/[id]/confirmation` tidak ada di Next.js server
**Solusi:**
Pastikan Next.js project punya file:
```
public/.well-known/apple-app-site-association
public/.well-known/assetlinks.json
```
Dan API route untuk handle:
```
pages/api/event/[id]/confirmation.ts
```
## 📄 File Configuration
### app.config.js - iOS
```javascript
ios: {
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
}
```
### app.config.js - Android
```javascript
android: {
intentFilters: [
{
action: "VIEW",
autoVerify: true,
data: [
{
scheme: "https",
host: "cld-dkr-staging-hipmi.wibudev.com",
pathPrefix: "/",
},
],
category: ["BROWSABLE", "DEFAULT"],
},
],
}
```
### apple-app-site-association (Next.js)
```json
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.com.anonymous.hipmi-mobile",
"paths": ["/event/*/confirmation"]
}
]
}
}
```
### assetlinks.json (Next.js)
```json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.bip.hipmimobileapp",
"sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"]
}
}
]
```
## 🎓 Best Practices
1. **Development**: Gunakan Custom Scheme untuk testing cepat
2. **Staging**: Gunakan HTTPS link dengan domain staging
3. **Production**: Gunakan HTTPS link dengan domain production
4. **Testing**: Selalu test di physical device, bukan simulator
5. **Debugging**: Enable logging di confirmation page untuk track deep link
## 🔗 Related Files
- `screens/Admin/Event/EventDetailQRCode.tsx` - QR Code generator
- `app/(application)/(user)/event/[id]/confirmation.tsx` - Confirmation page
- `app.config.js` - App configuration
- `service/api-config.ts` - API configuration (DEEP_LINK_URL)
## 📞 Support
Jika masih ada masalah:
1. Cek logs di console
2. Test manual dengan adb/xcrun
3. Verify .well-known files dengan curl
4. Pastikan app rebuild setelah perubahan config

View File

@@ -55,10 +55,10 @@ Component yang digunakan: components/_ShareComponent/NewWrapper.tsx
<!-- START Prompt Admin Refactoring --> <!-- START Prompt Admin Refactoring -->
<!-- Pindah kode ke Screen Component --> <!-- Pindah kode ke Screen Component -->
File source: app/(application)/admin/event/[id]/[status]/index.tsx File source: app/(application)/(user)/portofolio/[id]/create.tsx
Folder tujuan: screens/Admin/Event Folder tujuan: screens/Portofolio
Nama file utama: ScreenEventDetail.tsx Nama file utama: ScreenPortofolioCreate.tsx
Nama function utama: Admin_ScreenEventDetail Nama function utama: Admin_ScreenPortofolioCreate
File komponen wrapper: components/_ShareComponent/NewWrapper.tsx 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" 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"

View File

@@ -183,6 +183,20 @@
FB0CB57BF4D74C1D87C2036C /* Remove signature files (Xcode workaround) */, FB0CB57BF4D74C1D87C2036C /* Remove signature files (Xcode workaround) */,
14B3DE54EE4049AEB1EADA6B /* Remove signature files (Xcode workaround) */, 14B3DE54EE4049AEB1EADA6B /* Remove signature files (Xcode workaround) */,
B4CF5E09DBB44A4FB9CB91B9 /* Remove signature files (Xcode workaround) */, B4CF5E09DBB44A4FB9CB91B9 /* Remove signature files (Xcode workaround) */,
C894BD25C8224984AAD73398 /* Remove signature files (Xcode workaround) */,
F0C608193824414E93E23BC7 /* Remove signature files (Xcode workaround) */,
A3E2EDBCFB514A6487E28BEC /* Remove signature files (Xcode workaround) */,
0D62979D96BF4B99AB9FBE7C /* Remove signature files (Xcode workaround) */,
49B80EF12BE8476C86534CEA /* Remove signature files (Xcode workaround) */,
6218417B3C954EFF9B5F4853 /* Remove signature files (Xcode workaround) */,
7B5AE3770142492D84AEAAB3 /* Remove signature files (Xcode workaround) */,
0F1E3753571D42AB932C4F72 /* Remove signature files (Xcode workaround) */,
B020BDE42E304FBD99BD2279 /* Remove signature files (Xcode workaround) */,
6ECA5F81B0BC4C70A91BE265 /* Remove signature files (Xcode workaround) */,
44265583B67C48F2A24BA93E /* Remove signature files (Xcode workaround) */,
D5CA1D54CFF74AB4B8B5B583 /* Remove signature files (Xcode workaround) */,
97C01196E2194AF5A13C7773 /* Remove signature files (Xcode workaround) */,
EB19F4C53C8B434CBAD50897 /* Remove signature files (Xcode workaround) */,
); );
buildRules = ( buildRules = (
); );
@@ -995,6 +1009,244 @@
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\"; rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
"; ";
}; };
C894BD25C8224984AAD73398 /* 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\";
";
};
F0C608193824414E93E23BC7 /* 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\";
";
};
A3E2EDBCFB514A6487E28BEC /* 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\";
";
};
0D62979D96BF4B99AB9FBE7C /* 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\";
";
};
49B80EF12BE8476C86534CEA /* 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\";
";
};
6218417B3C954EFF9B5F4853 /* 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\";
";
};
7B5AE3770142492D84AEAAB3 /* 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\";
";
};
0F1E3753571D42AB932C4F72 /* 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\";
";
};
B020BDE42E304FBD99BD2279 /* 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\";
";
};
6ECA5F81B0BC4C70A91BE265 /* 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\";
";
};
44265583B67C48F2A24BA93E /* 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\";
";
};
D5CA1D54CFF74AB4B8B5B583 /* 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\";
";
};
97C01196E2194AF5A13C7773 /* 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\";
";
};
EB19F4C53C8B434CBAD50897 /* 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 */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */

View File

@@ -6,7 +6,7 @@
<string>development</string> <string>development</string>
<key>com.apple.developer.associated-domains</key> <key>com.apple.developer.associated-domains</key>
<array> <array>
<string>applinks:cld-dkr-staging-hipmi.wibudev.com</string> <string>applinks:cld-dkr-hipmi-stg.wibudev.com</string>
</array> </array>
</dict> </dict>
</plist> </plist>

View File

@@ -39,7 +39,7 @@
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>3</string> <string>5</string>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>

View File

@@ -4,10 +4,16 @@ import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { useNotificationStore } from "@/hooks/use-notification-store"; import { useNotificationStore } from "@/hooks/use-notification-store";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router"; import { router } from "expo-router";
import { useEffect } from "react";
import { Text, View } from "react-native"; import { Text, View } from "react-native";
export default function AdminNotificationBell() { export default function AdminNotificationBell() {
const { unreadCount } = useNotificationStore(); const { unreadCount, syncUnreadCount } = useNotificationStore();
useEffect(() => {
console.log("Syncing unread count");
syncUnreadCount();
}, [syncUnreadCount]);
return ( return (
<View style={{ position: "relative" }}> <View style={{ position: "relative" }}>

View File

@@ -1,26 +1,93 @@
import { BaseBox, LoaderCustom, Spacing, StackCustom, TextCustom } from "@/components"; import {
BaseBox,
ButtonCustom,
LoaderCustom,
Spacing,
StackCustom,
TextCustom,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { BASE_URL } from "@/service/api-config";
import { useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { StyleSheet } from "react-native";
import QRCode from "react-native-qrcode-svg"; import QRCode from "react-native-qrcode-svg";
interface EventDetailQRCodeProps { interface EventDetailQRCodeProps {
qrValue: string; userId: string;
isLoading: boolean; isLoading: boolean;
} }
export function EventDetailQRCode({ qrValue, isLoading }: EventDetailQRCodeProps) { export function EventDetailQRCode({
userId,
isLoading,
}: EventDetailQRCodeProps) {
const { id } = useLocalSearchParams();
const [useHttpsLink, setUseHttpsLink] = useState(true);
// HTTPS link untuk Universal Links (iOS) dan App Links (Android)
// Ini akan membuka file .well-known di Next.js server Anda
const httpsLink = `https://cld-dkr-hipmi-stg.wibudev.com/event/${id}/confirmation?userId=${userId}`;
// Custom scheme link untuk fallback atau testing tanpa universal links
const deepLinkURL = `${BASE_URL}/event/${id}/confirmation?userId=${userId}`;
// Toggle antara HTTPS link dan custom scheme
const qrValue = useHttpsLink ? httpsLink : deepLinkURL;
return ( return (
<BaseBox> <BaseBox>
<StackCustom style={{ alignItems: "center" }}> <StackCustom style={{ alignItems: "center" }}>
<TextCustom bold>QR Code Event</TextCustom> <TextCustom bold>QR Code Event</TextCustom>
{isLoading ? ( {isLoading ? <LoaderCustom /> : <QRCode value={qrValue} size={200} />}
<LoaderCustom />
) : (
<QRCode
value={qrValue}
size={200}
/>
)}
</StackCustom> </StackCustom>
<Spacing /> <Spacing />
<TextCustom color="gray" align="center" size="small">
{qrValue}
</TextCustom>
<Spacing />
<StackCustom direction="row" gap="sm">
<ButtonCustom
onPress={() => setUseHttpsLink(true)}
backgroundColor={useHttpsLink ? MainColor.yellow : "transparent"}
textColor={useHttpsLink ? MainColor.black : MainColor.yellow}
style={[
stylesButton.smallButton,
useHttpsLink && stylesButton.border,
]}
>
HTTPS
</ButtonCustom>
<ButtonCustom
onPress={() => setUseHttpsLink(false)}
backgroundColor={!useHttpsLink ? MainColor.yellow : "transparent"}
textColor={!useHttpsLink ? MainColor.black : MainColor.yellow}
style={[
stylesButton.smallButton,
!useHttpsLink && stylesButton.border,
]}
>
Custom Scheme
</ButtonCustom>
</StackCustom>
<Spacing />
<TextCustom color="gray" align="center" size={"small"}>
{useHttpsLink
? "✅ Testing Universal Links/App Links (butuh .well-known config)"
: "🔧 Testing langsung (tanpa domain verification)"}
</TextCustom>
</BaseBox> </BaseBox>
); );
} }
const stylesButton = StyleSheet.create({
smallButton: {
paddingHorizontal: 12,
paddingVertical: 6,
minWidth: 140,
},
border: {
borderWidth: 1,
borderColor: MainColor.yellow,
},
});

View File

@@ -1,22 +1,22 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { ActionIcon, AlertDefaultSystem } from "@/components"; import { ActionIcon, AlertDefaultSystem, Spacing } from "@/components";
import { IconDot } from "@/components/_Icon/IconComponent"; import { IconDot } from "@/components/_Icon/IconComponent";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject"; import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import ReportBox from "@/components/Box/ReportBox";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import ReportBox from "@/components/Box/ReportBox";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus"; import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
import { apiAdminEventById } from "@/service/api-admin/api-admin-event"; 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 { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
import { BoxEventDetail } from "./BoxEventDetail"; import { BoxEventDetail } from "./BoxEventDetail";
import { EventDetailDrawer } from "./EventDetailDrawer"; import { EventDetailDrawer } from "./EventDetailDrawer";
import { EventDetailQRCode } from "./EventDetailQRCode"; import { EventDetailQRCode } from "./EventDetailQRCode";
import { View } from "react-native";
export function Admin_ScreenEventDetail() { export function Admin_ScreenEventDetail() {
const { user } = useAuth(); const { user } = useAuth();
@@ -25,11 +25,6 @@ export function Admin_ScreenEventDetail() {
const [data, setData] = useState<any | null>(null); const [data, setData] = useState<any | null>(null);
const [loadData, setLoadData] = useState(false); 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( useFocusEffect(
useCallback(() => { useCallback(() => {
onLoadData(); onLoadData();
@@ -140,7 +135,11 @@ export function Admin_ScreenEventDetail() {
<> <>
<NewWrapper <NewWrapper
headerComponent={headerComponent} headerComponent={headerComponent}
footerComponent={footerComponent} // footerComponent={
// <View style={{ paddingInline: 8 }}>
// {footerComponent}
// </View>
// }
> >
<BoxEventDetail data={data} status={status as string} /> <BoxEventDetail data={data} status={status as string} />
@@ -149,8 +148,11 @@ export function Admin_ScreenEventDetail() {
)} )}
{(status === "publish" || status === "history") && ( {(status === "publish" || status === "history") && (
<EventDetailQRCode qrValue={isDevLink} isLoading={loadData} /> <EventDetailQRCode userId={user?.id || ""} isLoading={loadData} />
)} )}
{footerComponent}
<Spacing />
</NewWrapper> </NewWrapper>
<EventDetailDrawer <EventDetailDrawer

View File

@@ -9,6 +9,7 @@ import {
StackCustom, StackCustom,
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconPlus } from "@/components/_Icon"; import { IconPlus } from "@/components/_Icon";
import { IconDot } from "@/components/_Icon/IconComponent"; import { IconDot } from "@/components/_Icon/IconComponent";
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent"; import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
@@ -121,12 +122,16 @@ export default function Admin_ScreenNotification() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Admin Notifikasi", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Admin Notifikasi"
<IconDot left={<BackButton />}
color={MainColor.yellow} right={
onPress={() => setOpenDrawer(true)} <IconDot
color={MainColor.yellow}
onPress={() => setOpenDrawer(true)}
/>
}
/> />
), ),
}} }}

View File

@@ -11,6 +11,7 @@ import {
} from "@/components"; } from "@/components";
import { IconPlus } from "@/components/_Icon"; import { IconPlus } from "@/components/_Icon";
import { IconDot } from "@/components/_Icon/IconComponent"; import { IconDot } from "@/components/_Icon/IconComponent";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { AccentColor, MainColor } from "@/constants/color-palet"; import { AccentColor, MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL, PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value"; import { ICON_SIZE_SMALL, PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers"; import { createPaginationComponents } from "@/helpers/paginationHelpers";
@@ -169,12 +170,16 @@ export default function Admin_ScreenNotification2() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Admin Notifikasi", // title: "Admin Notifikasi",
headerLeft: () => <BackButton />, header: () => (
headerRight: () => ( <AppHeader
<IconDot title="Admin Notifikasi"
color={MainColor.yellow} right={
onPress={() => setOpenDrawer(true)} <IconDot
color={MainColor.yellow}
onPress={() => setOpenDrawer(true)}
/>
}
/> />
), ),
}} }}

View File

@@ -21,10 +21,10 @@ export default function VerificationView() {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [recodeOtp, setRecodeOtp] = useState<boolean>(false); const [recodeOtp, setRecodeOtp] = useState<boolean>(false);
// 🔑 DETEKSI MODE REVIEW (HANYA UNTUK NOMOR DEMO & PRODUCTION) // 🔑 DETEKSI MODE REVIEW (HANYA UNTUK NOMOR DEMO & DEVELOPMENT BUILD)
// Menggunakan Constants.expoConfig untuk mendeteksi development build
const isReviewMode = const isReviewMode =
typeof window !== "undefined" && // pastikan di browser/production process.env.NODE_ENV === "development" &&
process.env.NODE_ENV === "production" &&
nomor === "6282340374412"; nomor === "6282340374412";
// --- Context --- // --- Context ---
@@ -37,10 +37,6 @@ export default function VerificationView() {
// Hanya jalankan logika OTP normal jika BUKAN review mode // Hanya jalankan logika OTP normal jika BUKAN review mode
onLoadCheckCodeOtp(); onLoadCheckCodeOtp();
} }
console.log("[NODE_ENV]:", process.env.NODE_ENV);
console.log("[isReviewMode]:", isReviewMode);
console.log("[nomor]:", nomor);
}, [recodeOtp, isReviewMode]); }, [recodeOtp, isReviewMode]);
async function onLoadCheckCodeOtp() { async function onLoadCheckCodeOtp() {
@@ -85,29 +81,30 @@ export default function VerificationView() {
const handleVerification = async () => { const handleVerification = async () => {
if (isReviewMode) { if (isReviewMode) {
// ✅ VERIFIKASI OTOMATIS UNTUK APPLE REVIEW // ✅ VERIFIKASI OTOMATIS UNTUK APPLE REVIEW (Development Only)
if (inputOtp === "1234") { if (inputOtp !== "1234") {
try {
await validateOtp(nomor as string);
return;
} catch (error) {
console.log("Error verification", error);
Toast.show({ type: "error", text1: "Gagal verifikasi" });
}
} else {
Toast.show({ type: "error", text1: "Kode OTP tidak sesuai" }); Toast.show({ type: "error", text1: "Kode OTP tidak sesuai" });
return;
}
try {
await validateOtp(nomor as string, inputOtp);
return;
} catch (error) {
console.log("Error verification", error);
Toast.show({ type: "error", text1: "Gagal verifikasi" });
} }
return;
} }
// 🔁 VERIFIKASI NORMAL (untuk pengguna sungguhan) // 🔁 VERIFIKASI NORMAL (untuk pengguna sungguhan)
try { try {
await validateOtp(nomor as string); await validateOtp(nomor as string, inputOtp);
return return;
} catch (error) { } catch (error: any) {
console.log("Error verification", error); console.log("Error verification", error);
Toast.show({ type: "error", text1: "Gagal verifikasi" }); Toast.show({
type: "error",
text1: error.response?.data?.message || "Gagal verifikasi",
});
} }
}; };

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { BackButton, DrawerCustom, MenuDrawerDynamicGrid } from "@/components"; import { BackButton, DrawerCustom, MenuDrawerDynamicGrid } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconPlus } from "@/components/_Icon"; import { IconPlus } from "@/components/_Icon";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
@@ -52,8 +53,12 @@ export default function Donation_ScreenListOfNews({
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Daftar Kabar", header: () => (
headerLeft: () => <BackButton />, <AppHeader
title="Daftar Kabar"
left={<BackButton />}
/>
),
}} }}
/> />
<NewWrapper <NewWrapper

View File

@@ -5,6 +5,7 @@ import {
DrawerCustom, DrawerCustom,
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconPlus } from "@/components/_Icon"; import { IconPlus } from "@/components/_Icon";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value"; import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
@@ -61,9 +62,13 @@ export default function Donation_ScreenRecapOfNews({
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Rekap Kabar", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />, title="Rekap Kabar"
left={<BackButton />}
right={<DotButton onPress={() => setOpenDrawer(true)} />}
/>
),
}} }}
/> />
<NewWrapper <NewWrapper

View File

@@ -7,6 +7,7 @@ import {
LoaderCustom, LoaderCustom,
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { apiForumGetAll } from "@/service/api-client/api-forum"; import { apiForumGetAll } from "@/service/api-client/api-forum";
import { apiUser } from "@/service/api-client/api-user"; import { apiUser } from "@/service/api-client/api-user";
@@ -54,13 +55,17 @@ export default function Forum_ViewBeranda() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Forum", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Forum"
<AvatarComp left={<BackButton />}
fileId={dataUser?.Profile?.imageId} right={
size="base" <AvatarComp
href={`/forum/${user?.id}/forumku`} fileId={dataUser?.Profile?.imageId}
size="base"
href={`/forum/${user?.id}/forumku`}
/>
}
/> />
), ),
}} }}

View File

@@ -8,6 +8,7 @@ import {
StackCustom, StackCustom,
TextCustom, // ← gunakan NewWrapper yang sudah diperbaiki TextCustom, // ← gunakan NewWrapper yang sudah diperbaiki
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import SkeletonCustom from "@/components/_ShareComponent/SkeletonCustom"; import SkeletonCustom from "@/components/_ShareComponent/SkeletonCustom";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
@@ -155,13 +156,17 @@ export default function Forum_ViewBeranda2() {
{/* 🔹 Header Navigation */} {/* 🔹 Header Navigation */}
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Forum", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Forum"
<AvatarComp left={<BackButton />}
fileId={dataUser?.Profile?.imageId} right={
size="base" <AvatarComp
href={`/forum/${user?.id}/forumku`} fileId={dataUser?.Profile?.imageId}
size="base"
href={`/forum/${user?.id}/forumku`}
/>
}
/> />
), ),
}} }}

View File

@@ -4,6 +4,7 @@ import {
FloatingButton, FloatingButton,
SearchInput, SearchInput,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { createPaginationComponents } from "@/helpers/paginationHelpers"; import { createPaginationComponents } from "@/helpers/paginationHelpers";
@@ -84,18 +85,22 @@ export default function Forum_ViewBeranda3() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Forum", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Forum"
<TouchableOpacity left={<BackButton />}
onPress={() => router.navigate(`/forum/${user?.id}/forumku`)} right={
> <TouchableOpacity
<AvatarComp onPress={() => router.navigate(`/forum/${user?.id}/forumku`)}
fileId={dataUser?.Profile?.imageId} >
size="base" <AvatarComp
href={`/forum/${user?.id}/forumku`} fileId={dataUser?.Profile?.imageId}
/> size="base"
</TouchableOpacity> href={`/forum/${user?.id}/forumku`}
/>
</TouchableOpacity>
}
/>
), ),
}} }}
/> />

View File

@@ -6,6 +6,7 @@ import {
DrawerCustom, DrawerCustom,
MenuDrawerDynamicGrid MenuDrawerDynamicGrid
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconEdit } from "@/components/_Icon"; import { IconEdit } from "@/components/_Icon";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
@@ -125,14 +126,18 @@ export default function Investment_ScreenRecapOfDocument() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Rekap Dokumen", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Rekap Dokumen"
<DotButton left={<BackButton />}
onPress={() => { right={
setOpenDrawer(true); <DotButton
setOpenDrawerBox(false); onPress={() => {
}} setOpenDrawer(true);
setOpenDrawerBox(false);
}}
/>
}
/> />
), ),
}} }}

View File

@@ -7,6 +7,7 @@ import {
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconPlus } from "@/components/_Icon"; import { IconPlus } from "@/components/_Icon";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { usePagination } from "@/hooks/use-pagination"; import { usePagination } from "@/hooks/use-pagination";
@@ -64,9 +65,13 @@ export default function Investment_ScreenListOfNews({
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Daftar Berita", header: () => (
headerLeft: () => <BackButton />, <AppHeader
// headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />, title="Daftar Berita"
left={<BackButton />}
// headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
/>
),
}} }}
/> />

View File

@@ -9,6 +9,7 @@ import {
Spacing, Spacing,
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconPlus } from "@/components/_Icon"; import { IconPlus } from "@/components/_Icon";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { usePagination } from "@/hooks/use-pagination"; import { usePagination } from "@/hooks/use-pagination";
@@ -66,9 +67,13 @@ export default function Investment_ScreenRecapOfNews({
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Rekap Berita", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />, title="Rekap Berita"
left={<BackButton />}
right={<DotButton onPress={() => setOpenDrawer(true)} />}
/>
),
}} }}
/> />
<NewWrapper <NewWrapper

View File

@@ -11,6 +11,7 @@ import {
StackCustom, StackCustom,
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconDot } from "@/components/_Icon/IconComponent"; import { IconDot } from "@/components/_Icon/IconComponent";
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent"; import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
import NoDataText from "@/components/_ShareComponent/NoDataText"; import NoDataText from "@/components/_ShareComponent/NoDataText";
@@ -217,12 +218,16 @@ export default function ScreenNotification_V1() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Notifikasi", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Notifikasi"
<IconDot left={<BackButton />}
color={MainColor.yellow} right={
onPress={() => setOpenDrawer(true)} <IconDot
color={MainColor.yellow}
onPress={() => setOpenDrawer(true)}
/>
}
/> />
), ),
}} }}

View File

@@ -10,6 +10,7 @@ import {
StackCustom, StackCustom,
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import { IconDot } from "@/components/_Icon/IconComponent"; import { IconDot } from "@/components/_Icon/IconComponent";
import { AccentColor, MainColor } from "@/constants/color-palet"; import { AccentColor, MainColor } from "@/constants/color-palet";
import { import {
@@ -182,12 +183,16 @@ export default function ScreenNotification_V2() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Notifikasi", header: () => (
headerLeft: () => <BackButton />, <AppHeader
headerRight: () => ( title="Notifikasi"
<IconDot left={<BackButton />}
color={MainColor.yellow} right={
onPress={() => setOpenDrawer(true)} <IconDot
color={MainColor.yellow}
onPress={() => setOpenDrawer(true)}
/>
}
/> />
), ),
}} }}

View File

@@ -0,0 +1,340 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
ActionIcon,
ButtonCenteredOnly,
CenterCustom,
InformationBox,
NewWrapper,
SelectCustom,
Spacing,
StackCustom,
TextAreaCustom,
TextCustom,
TextInputCustom,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
import DUMMY_IMAGE from "@/constants/dummy-image-value";
import Portofolio_ButtonCreate from "@/screens/Portofolio/ButtonCreatePortofolio";
import {
apiMasterBidangBisnis,
apiMasterSubBidangBisnis,
} from "@/service/api-client/api-master";
import {
IMasterBidangBisnis,
IMasterSubBidangBisnis,
} from "@/types/Type-Master";
import pickImage from "@/utils/pickImage";
import { Ionicons } from "@expo/vector-icons";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Text, View } from "react-native";
import PhoneInput, { ICountry } from "react-native-international-phone-number";
import { Avatar } from "react-native-paper";
export function Admin_ScreenPortofolioCreate() {
const { id } = useLocalSearchParams();
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
const [inputValue, setInputValue] = useState<string>("");
const [data, setData] = useState({
namaBisnis: "",
masterBidangBisnisId: "",
alamatKantor: "",
tlpn: "",
deskripsi: "",
});
const [imageUri, setImageUri] = useState<string | null>(null);
const [bidangBisnis, setBidangBisnis] = useState<IMasterBidangBisnis[]>([]);
const [subBidangBisnis, setSubBidangBisnis] = useState<
IMasterSubBidangBisnis[]
>([]);
const [selectedSubBidang, setSelectedSubBidang] = useState<string[]>([]);
const [listSubBidangSelected, setListSubBidangSelected] = useState([
{
id: "",
},
]);
const [dataMedsos, setDataMedsos] = useState({
facebook: "",
twitter: "",
instagram: "",
youtube: "",
tiktok: "",
});
const [isLoadingCreate, setIsLoadingCreate] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadMaster();
onLoadMasterSubBidangBisnis();
}, [])
);
const onLoadMaster = async () => {
try {
const response = await apiMasterBidangBisnis();
setBidangBisnis(response.data);
} catch (error) {
setBidangBisnis([]);
console.log("Error onLoadMasterBidangBisnis", error);
}
};
const onLoadMasterSubBidangBisnis = async () => {
try {
const response = await apiMasterSubBidangBisnis({});
setSubBidangBisnis(response.data);
} catch (error) {
setSubBidangBisnis([]);
console.log("Error onLoadMasterSubBidangBisnis", error);
}
};
const handlerSelectedSubBidang = ({ id }: { id: string }) => {
const selectedList = subBidangBisnis?.filter(
(item) => (item?.masterBidangBisnisId as any) === id
);
setSelectedSubBidang(selectedList as any[]);
};
const handleInputValue = (phoneNumber: string) => {
setInputValue(phoneNumber);
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, "");
const realNumber = callingCode + fixNumber;
setData({ ...data, tlpn: realNumber });
};
const handleSelectedCountry = (country: ICountry) => {
setSelectedCountry(country);
};
return (
<NewWrapper
footerComponent={
<Portofolio_ButtonCreate
id={id as string}
data={data}
dataMedsos={dataMedsos}
imageUri={imageUri}
subBidangSelected={listSubBidangSelected}
isLoadingCreate={isLoadingCreate}
setIsLoadingCreate={setIsLoadingCreate}
/>
}
>
<StackCustom gap="xs">
<InformationBox text="Lengkapi data bisnis anda." />
<TextInputCustom
required
label="Nama Bisnis"
placeholder="Masukkan nama bisnis"
onChangeText={(value: any) => setData({ ...data, namaBisnis: value })}
/>
<SelectCustom
label="Bidang Usaha"
required
data={bidangBisnis.map((item) => ({
label: item.name,
value: item.id,
}))}
value={data.masterBidangBisnisId}
onChange={(value) => {
const isSameBidang = data.masterBidangBisnisId === value;
if (!isSameBidang) {
setListSubBidangSelected([{ id: "" }]);
}
setData({ ...(data as any), masterBidangBisnisId: value });
handlerSelectedSubBidang({ id: value as string });
}}
/>
{listSubBidangSelected.map((item, index) => (
<SelectCustom
key={index}
disabled={data.masterBidangBisnisId === ""}
label="Sub Bidang Usaha"
required
data={_.map(selectedSubBidang as any)
.filter((option: any) => {
const selectedValues = listSubBidangSelected.map((s) => s.id);
return (
option.id === item.id ||
!selectedValues.includes(option.id)
);
})
.map((e: any) => ({
value: e.id,
label: e.name,
}))}
value={item.id || null}
onChange={(value) => {
const list = _.clone(listSubBidangSelected);
list[index].id = value as any;
setListSubBidangSelected(list);
}}
/>
))}
<CenterCustom>
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
<ActionIcon
disabled={selectedSubBidang.length === listSubBidangSelected.length}
onPress={() => {
setListSubBidangSelected([
...listSubBidangSelected,
{ id: "" },
]);
}}
icon={
<Ionicons
name="add-circle-outline"
size={ICON_SIZE_XLARGE}
color={MainColor.black}
/>
}
size="xl"
/>
<ActionIcon
disabled={listSubBidangSelected.length <= 1}
onPress={() => {
const list = _.clone(listSubBidangSelected);
list.pop();
setListSubBidangSelected(list);
}}
icon={
<Ionicons
name="remove-circle-outline"
size={ICON_SIZE_XLARGE}
color={MainColor.black}
/>
}
size="xl"
/>
</View>
</CenterCustom>
<Spacing />
<View>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
Nomor Telepon
</TextCustom>
<Text style={{ color: "red" }}> *</Text>
</View>
<Spacing height={5} />
<PhoneInput
value={inputValue}
onChangePhoneNumber={handleInputValue}
selectedCountry={selectedCountry}
onChangeSelectedCountry={handleSelectedCountry}
defaultCountry="ID"
placeholder="xxx-xxx-xxx"
/>
</View>
<Spacing />
<TextInputCustom
required
label="Alamat Bisnis"
placeholder="Masukkan alamat bisnis"
onChangeText={(value: any) =>
setData({ ...data, alamatKantor: value })
}
/>
<TextAreaCustom
label="Deskripsi Bisnis"
placeholder="Masukkan deskripsi bisnis"
value={data.deskripsi}
onChangeText={(value: any) => setData({ ...data, deskripsi: value })}
autosize
minRows={2}
maxRows={5}
required
showCount
maxLength={1000}
/>
<Spacing />
<InformationBox text="Upload logo bisnis anda untuk di tampilaka pada portofolio." />
<CenterCustom>
<Avatar.Image
source={imageUri ? { uri: imageUri } : DUMMY_IMAGE.dummy_image}
size={200}
/>
</CenterCustom>
<Spacing />
<ButtonCenteredOnly
icon="upload"
onPress={() => {
pickImage({
setImageUri,
});
}}
>
Upload
</ButtonCenteredOnly>
<Spacing height={40} />
<InformationBox text="Isi hanya pada sosial media yang anda miliki." />
<TextInputCustom
label="Tiktok"
placeholder="Masukkan username tiktok"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, tiktok: value })
}
/>
<TextInputCustom
label="Facebook"
placeholder="Masukkan username facebook"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, facebook: value })
}
/>
<TextInputCustom
label="Instagram"
placeholder="Masukkan username instagram"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, instagram: value })
}
/>
<TextInputCustom
label="Twitter"
placeholder="Masukkan username twitter"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, twitter: value })
}
/>
<TextInputCustom
label="Youtube"
placeholder="Masukkan username youtube"
onChangeText={(value: any) =>
setDataMedsos({ ...dataMedsos, youtube: value })
}
/>
</StackCustom>
</NewWrapper>
);
}

View File

@@ -45,9 +45,16 @@ export async function apiCheckCodeOtp({ kodeId }: { kodeId: string }) {
return response.data; return response.data;
} }
export async function apiValidationCode({ nomor }: { nomor: string }) { export async function apiValidationCode({
nomor,
code,
}: {
nomor: string;
code: string;
}) {
const response = await apiConfig.post(`/auth/mobile-validasi`, { const response = await apiConfig.post(`/auth/mobile-validasi`, {
nomor: nomor, nomor: nomor,
code: code,
}); });
return response.data; return response.data;
} }