Compare commits
3 Commits
clean-code
...
clean-code
| Author | SHA1 | Date | |
|---|---|---|---|
| 57c9215771 | |||
| 4efdbd3c7b | |||
| ad32eb6fe6 |
@@ -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>
|
||||||
|
|||||||
@@ -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: "4",
|
||||||
},
|
},
|
||||||
|
|
||||||
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: "/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
apiEventConfirmationAction,
|
apiEventConfirmationAction,
|
||||||
@@ -60,7 +60,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 +113,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 +218,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 (
|
||||||
@@ -261,17 +266,15 @@ export default function UserEventConfirmation() {
|
|||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
title: "Konfirmasi Event",
|
title: "Konfirmasi Event",
|
||||||
// headerLeft: () => (
|
headerLeft: () => (
|
||||||
// <Ionicons
|
<Ionicons
|
||||||
// name="arrow-back"
|
name="arrow-back"
|
||||||
// size={20}
|
size={20}
|
||||||
// color={MainColor.yellow}
|
color={MainColor.yellow}
|
||||||
// onPress={() =>
|
onPress={() => router.navigate("/")}
|
||||||
// router.navigate("/(application)/(user)/event/create")
|
/>
|
||||||
// }
|
),
|
||||||
// />
|
}}
|
||||||
// ),
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>{handlerReturn()}</ViewWrapper>
|
<ViewWrapper>{handlerReturn()}</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
@@ -497,7 +500,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 = ({
|
||||||
|
|||||||
@@ -17,6 +17,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,
|
||||||
|
|||||||
@@ -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
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) */,
|
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) */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -995,6 +1001,108 @@
|
|||||||
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\";
|
||||||
|
";
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>3</string>
|
<string>4</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
|||||||
@@ -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" }}>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user