Merge pull request 'clean-code/11-mar-26' (#62) from clean-code/11-mar-26 into staging
Reviewed-on: #62
This commit is contained in:
@@ -37,7 +37,7 @@
|
||||
</intent-filter>
|
||||
<intent-filter android:autoVerify="true" data-generated="true">
|
||||
<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.DEFAULT"/>
|
||||
</intent-filter>
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
// app.config.js
|
||||
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 {
|
||||
name: "HIPMI Badung Connect",
|
||||
slug: "hipmi-mobile",
|
||||
@@ -20,8 +31,10 @@ export default {
|
||||
NSLocationWhenInUseUsageDescription:
|
||||
"Aplikasi membutuhkan akses lokasi untuk menampilkan peta.",
|
||||
},
|
||||
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
|
||||
buildNumber: "3",
|
||||
associatedDomains: [
|
||||
"applinks:cld-dkr-hipmi-stg.wibudev.com",
|
||||
],
|
||||
buildNumber: "4",
|
||||
},
|
||||
|
||||
android: {
|
||||
@@ -41,7 +54,7 @@ export default {
|
||||
data: [
|
||||
{
|
||||
scheme: "https",
|
||||
host: "cld-dkr-staging-hipmi.wibudev.com",
|
||||
host: "cld-dkr-hipmi-stg.wibudev.com",
|
||||
pathPrefix: "/",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import {
|
||||
apiEventConfirmationAction,
|
||||
@@ -60,7 +60,7 @@ export default function UserEventConfirmation() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
checkTokenAndDataParticipants() || console.log("Token is null");
|
||||
}, [token, id, user?.id])
|
||||
}, [token, id, user?.id]),
|
||||
);
|
||||
|
||||
const checkTokenAndDataParticipants = async () => {
|
||||
@@ -113,7 +113,7 @@ export default function UserEventConfirmation() {
|
||||
confirmationStart,
|
||||
confirmationEnd,
|
||||
null,
|
||||
"[]"
|
||||
"[]",
|
||||
);
|
||||
|
||||
// --- [4] Status waktu event (untuk pesan UI) ---
|
||||
@@ -218,9 +218,14 @@ export default function UserEventConfirmation() {
|
||||
if (isWithinConfirmationWindow) {
|
||||
if (konfirmasi === false) {
|
||||
return (
|
||||
<TamplateBox data={data}>
|
||||
<TamplateText text="Konfirmasi Kehadiran" />
|
||||
</TamplateBox>
|
||||
// <TamplateBox data={data}>
|
||||
// <TamplateText text="Konfirmasi Kehadiran" />
|
||||
// </TamplateBox>
|
||||
<UserParticipan_And_DuringEvent
|
||||
id={data.id}
|
||||
userId={user?.id as string}
|
||||
data={data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
@@ -261,17 +266,15 @@ export default function UserEventConfirmation() {
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Konfirmasi Event",
|
||||
// headerLeft: () => (
|
||||
// <Ionicons
|
||||
// name="arrow-back"
|
||||
// size={20}
|
||||
// color={MainColor.yellow}
|
||||
// onPress={() =>
|
||||
// router.navigate("/(application)/(user)/event/create")
|
||||
// }
|
||||
// />
|
||||
// ),
|
||||
}}
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.navigate("/")}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>{handlerReturn()}</ViewWrapper>
|
||||
</>
|
||||
@@ -497,7 +500,6 @@ const UserNotParticipan_And_DuringEvent = ({
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// 🟡 ZONA ACARA BERLANGSUN
|
||||
// User sudah terdaftar & Event sedang berlangsung & user harus konfirmasi
|
||||
const UserParticipan_And_DuringEvent = ({
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
ICON_SIZE_XLARGE,
|
||||
} from "@/constants/constans-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||
import AdminNotificationBell from "@/screens/Admin/AdminNotificationBell";
|
||||
import {
|
||||
adminListMenu,
|
||||
|
||||
@@ -22,7 +22,7 @@ type AuthContextType = {
|
||||
isAdmin: boolean;
|
||||
isUserActive: boolean;
|
||||
loginWithNomor: (nomor: string) => Promise<boolean>;
|
||||
validateOtp: (nomor: string) => Promise<any>;
|
||||
validateOtp: (nomor: string, code: string) => Promise<any>;
|
||||
logout: () => Promise<void>;
|
||||
registerUser: (userData: {
|
||||
username: string;
|
||||
@@ -97,10 +97,10 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
};
|
||||
|
||||
// --- 2. Validasi OTP & cek user ---
|
||||
const validateOtp = async (nomor: string) => {
|
||||
const validateOtp = async (nomor: string, code: string) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiValidationCode({ nomor: nomor });
|
||||
const response = await apiValidationCode({ nomor: nomor, code: code });
|
||||
const { token } = response;
|
||||
console.log("[RESPONSE VALIDASI OTP]", JSON.stringify(response, null, 2));
|
||||
|
||||
|
||||
253
docs/QR_CODE_TESTING.md
Normal file
253
docs/QR_CODE_TESTING.md
Normal 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
|
||||
@@ -183,6 +183,12 @@
|
||||
FB0CB57BF4D74C1D87C2036C /* Remove signature files (Xcode workaround) */,
|
||||
14B3DE54EE4049AEB1EADA6B /* 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) */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -995,6 +1001,108 @@
|
||||
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\";
|
||||
";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<string>development</string>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:cld-dkr-staging-hipmi.wibudev.com</string>
|
||||
<string>applinks:cld-dkr-hipmi-stg.wibudev.com</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -39,7 +39,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3</string>
|
||||
<string>4</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@@ -4,10 +4,16 @@ import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { useEffect } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
export default function AdminNotificationBell() {
|
||||
const { unreadCount } = useNotificationStore();
|
||||
const { unreadCount, syncUnreadCount } = useNotificationStore();
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Syncing unread count");
|
||||
syncUnreadCount();
|
||||
}, [syncUnreadCount]);
|
||||
|
||||
return (
|
||||
<View style={{ position: "relative" }}>
|
||||
|
||||
@@ -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";
|
||||
|
||||
interface EventDetailQRCodeProps {
|
||||
qrValue: string;
|
||||
userId: string;
|
||||
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 (
|
||||
<BaseBox>
|
||||
<StackCustom style={{ alignItems: "center" }}>
|
||||
<TextCustom bold>QR Code Event</TextCustom>
|
||||
{isLoading ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<QRCode
|
||||
value={qrValue}
|
||||
size={200}
|
||||
/>
|
||||
)}
|
||||
{isLoading ? <LoaderCustom /> : <QRCode value={qrValue} size={200} />}
|
||||
</StackCustom>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
const stylesButton = StyleSheet.create({
|
||||
smallButton: {
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
minWidth: 140,
|
||||
},
|
||||
border: {
|
||||
borderWidth: 1,
|
||||
borderColor: MainColor.yellow,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { ActionIcon, AlertDefaultSystem } from "@/components";
|
||||
import { ActionIcon, AlertDefaultSystem, Spacing } from "@/components";
|
||||
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
||||
import ReportBox from "@/components/Box/ReportBox";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import ReportBox from "@/components/Box/ReportBox";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
|
||||
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
|
||||
import { DEEP_LINK_URL } from "@/service/api-config";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
import { BoxEventDetail } from "./BoxEventDetail";
|
||||
import { EventDetailDrawer } from "./EventDetailDrawer";
|
||||
import { EventDetailQRCode } from "./EventDetailQRCode";
|
||||
import { View } from "react-native";
|
||||
|
||||
export function Admin_ScreenEventDetail() {
|
||||
const { user } = useAuth();
|
||||
@@ -25,11 +25,6 @@ export function Admin_ScreenEventDetail() {
|
||||
const [data, setData] = useState<any | null>(null);
|
||||
const [loadData, setLoadData] = useState(false);
|
||||
|
||||
const deepLinkURL = `${DEEP_LINK_URL}/event/${id}/confirmation?userId=${user?.id}`;
|
||||
const deepLinkURLDEV = `${DEEP_LINK_URL}/--/event/${id}/confirmation?userId=${user?.id}`;
|
||||
const isDevLink =
|
||||
process.env.NODE_ENV === "development" ? deepLinkURLDEV : deepLinkURL;
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
@@ -140,7 +135,11 @@ export function Admin_ScreenEventDetail() {
|
||||
<>
|
||||
<NewWrapper
|
||||
headerComponent={headerComponent}
|
||||
footerComponent={footerComponent}
|
||||
// footerComponent={
|
||||
// <View style={{ paddingInline: 8 }}>
|
||||
// {footerComponent}
|
||||
// </View>
|
||||
// }
|
||||
>
|
||||
<BoxEventDetail data={data} status={status as string} />
|
||||
|
||||
@@ -149,8 +148,11 @@ export function Admin_ScreenEventDetail() {
|
||||
)}
|
||||
|
||||
{(status === "publish" || status === "history") && (
|
||||
<EventDetailQRCode qrValue={isDevLink} isLoading={loadData} />
|
||||
<EventDetailQRCode userId={user?.id || ""} isLoading={loadData} />
|
||||
)}
|
||||
|
||||
{footerComponent}
|
||||
<Spacing />
|
||||
</NewWrapper>
|
||||
|
||||
<EventDetailDrawer
|
||||
|
||||
@@ -21,10 +21,10 @@ export default function VerificationView() {
|
||||
const [loading, setLoading] = 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 =
|
||||
typeof window !== "undefined" && // pastikan di browser/production
|
||||
process.env.NODE_ENV === "production" &&
|
||||
process.env.NODE_ENV === "development" &&
|
||||
nomor === "6282340374412";
|
||||
|
||||
// --- Context ---
|
||||
@@ -37,10 +37,6 @@ export default function VerificationView() {
|
||||
// Hanya jalankan logika OTP normal jika BUKAN review mode
|
||||
onLoadCheckCodeOtp();
|
||||
}
|
||||
|
||||
console.log("[NODE_ENV]:", process.env.NODE_ENV);
|
||||
console.log("[isReviewMode]:", isReviewMode);
|
||||
console.log("[nomor]:", nomor);
|
||||
}, [recodeOtp, isReviewMode]);
|
||||
|
||||
async function onLoadCheckCodeOtp() {
|
||||
@@ -85,29 +81,30 @@ export default function VerificationView() {
|
||||
|
||||
const handleVerification = async () => {
|
||||
if (isReviewMode) {
|
||||
// ✅ VERIFIKASI OTOMATIS UNTUK APPLE REVIEW
|
||||
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 {
|
||||
// ✅ VERIFIKASI OTOMATIS UNTUK APPLE REVIEW (Development Only)
|
||||
if (inputOtp !== "1234") {
|
||||
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)
|
||||
try {
|
||||
await validateOtp(nomor as string);
|
||||
return
|
||||
} catch (error) {
|
||||
await validateOtp(nomor as string, inputOtp);
|
||||
return;
|
||||
} catch (error: any) {
|
||||
console.log("Error verification", error);
|
||||
Toast.show({ type: "error", text1: "Gagal verifikasi" });
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: error.response?.data?.message || "Gagal verifikasi",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -45,9 +45,16 @@ export async function apiCheckCodeOtp({ kodeId }: { kodeId: string }) {
|
||||
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`, {
|
||||
nomor: nomor,
|
||||
code: code,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user