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
13 KiB
HIPMI Mobile Application - Development Context
Project Overview
HIPMI Mobile is a cross-platform mobile application built with Expo and React Native. The application is named "HIPMI Badung Connect" and serves as a platform for the HIPMI (Himpunan Pengusaha dan Pengusaha Indonesia) Badung chapter. It's designed to run on iOS, Android, and web platforms using a single codebase.
Key Technologies
- Framework: Expo (v54.0.0) with React Native (v0.81.5)
- Language: TypeScript
- Architecture: File-based routing with Expo Router
- State Management: Context API (AuthContext)
- UI Components: React Native Paper, custom components
- Maps Integration: Maplibre Maps for React Native (
@maplibre/maplibre-react-nativev10.4.2) - Push Notifications: React Native Firebase Messaging
- Build System: Metro bundler
- Package Manager: Bun
Project Structure
hipmi-mobile/
├── app/ # Main application screens and routing (Expo Router)
│ ├── _layout.tsx # Root layout component
│ ├── index.tsx # Entry point (Login screen)
│ └── (application)/ # Main app screens
│ ├── admin/ # Admin panel screens
│ ├── (user)/ # User screens
│ └── ...
├── components/ # Reusable UI components
│ ├── _ShareComponent/ # Shared components (NewWrapper, Admin components)
│ ├── _Icon/ # Icon components
│ └── ...
├── context/ # State management (AuthContext)
├── screens/ # Screen components organized by feature
│ ├── Admin/ # Admin panel screens
│ │ ├── Donation/ # Donation management screens
│ │ ├── Voting/ # Voting management screens
│ │ ├── Event/ # Event management screens
│ │ └── ...
│ ├── Authentication/ # Login, registration flows
│ ├── RootLayout/ # Root layout components
│ └── ...
├── service/ # API services and business logic
│ ├── api-admin/ # Admin API endpoints
│ ├── api-client/ # Client API endpoints
│ └── api-config.ts # Axios configuration
├── hooks/ # Custom React hooks
│ ├── use-pagination.tsx # Pagination hook
│ └── ...
├── helpers/ # Helper functions
│ ├── paginationHelpers.tsx # Pagination UI helpers
│ └── ...
├── types/ # TypeScript type definitions
├── utils/ # Utility functions
├── constants/ # Constants and configuration values
├── styles/ # Global styles
├── assets/ # Images, icons, and static assets
└── docs/ # Documentation files
Building and Running
Prerequisites
- Node.js: v18+ with Bun package manager
- Expo CLI: Installed globally or via npx
- iOS: Xcode (macOS only) for iOS simulator/builds
- Android: Android Studio for Android emulator/builds
Setup and Development
-
Install Dependencies
bun install -
Run Development Server
bun run start # or bunx expo start -
Platform-Specific Commands
# iOS Simulator bun run ios # or bunx expo start --ios # Android Emulator bun run android # or bunx expo start --android # Web Browser bun run web # or bunx expo start --web -
Linting
bun run lint
Build Commands
EAS Build (Production)
# Production build (App Store / Play Store)
eas build --profile production
# Preview build (Internal distribution)
eas build --profile preview
# Development build (Development client)
eas build --profile development
Local Native Builds
# Generate native folders (iOS & Android)
npx expo prebuild
# iOS specific
bunx expo prebuild --platform ios
open ios/HIPMIBadungConnect.xcworkspace
# Android specific
bunx expo prebuild --platform android
Version Management
# Patch version update
npm version patch
# Update iOS build number
bunx expo prebuild --platform ios
# Update Android version code
bunx expo prebuild --platform android
Android Debugging
# List connected devices
adb devices
# Install APK to device/emulator
adb install android/app/build/outputs/apk/debug/app-debug.apk
# Install to specific device
adb -s <device_id> install android/app/build/outputs/apk/debug/app-debug.apk
Environment Variables
Create a .env file in the project root with:
API_BASE_URL=https://your-api-base-url.com
BASE_URL=https://your-app-url.com
DEEP_LINK_URL=hipmimobile://
These are loaded in app.config.js and accessible via Constants.expoConfig.extra.
Architecture Patterns
1. Separation of Concerns
Route Files (app/) should be minimal (max 5 lines):
import { Admin_ScreenXXX } from "@/screens/Admin/XXX/ScreenXXX";
export default function AdminXXX() {
return <Admin_ScreenXXX />;
}
Screen Components (screens/) contain all business logic:
export function Admin_ScreenXXX() {
// Logic, hooks, state management
return <NewWrapper ... />;
}
2. Pagination Pattern
Using usePagination hook with infinite scroll:
const pagination = usePagination({
fetchFunction: async (page, searchQuery) => {
const response = await apiXXX({ page: String(page) });
if (response.success) {
return { data: response.data };
}
return { data: [] };
},
pageSize: PAGINATION_DEFAULT_TAKE, // 10
searchQuery: search,
dependencies: [dependency],
});
const { ListEmptyComponent, ListFooterComponent } =
createPaginationComponents({
loading: pagination.loading,
refreshing: pagination.refreshing,
listData: pagination.listData,
emptyMessage: "Belum ada data",
skeletonCount: PAGINATION_DEFAULT_TAKE,
});
3. Wrapper Components
NewWrapper (preferred for lists):
<NewWrapper
listData={pagination.listData}
renderItem={renderItem}
headerComponent={headerComponent}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
onEndReached={pagination.loadMore}
refreshControl={<RefreshControl ... />}
/>
AdminBasicBox (for card layouts):
<AdminBasicBox
onPress={() => router.push(`/path/${item.id}`)}
style={{ marginHorizontal: 10, marginVertical: 5 }}
>
<StackCustom gap={0}>
<GridSpan_4_8 label="Label" value={<TextCustom>Value</TextCustom>} />
</StackCustom>
</AdminBasicBox>
4. API Service Structure
// service/api-admin/api-xxx.ts
export async function apiXXX({ page = "1" }: { page?: string }) {
try {
const response = await apiConfig.get(`/mobile/admin/xxx?page=${page}`);
return response.data;
} catch (error) {
throw error;
}
}
Important: All list APIs should support pagination with page parameter (default: "1").
5. Authentication Flow
Managed by AuthContext:
loginWithNomor()- Send phone number, receive OTPvalidateOtp()- Validate OTP, get tokenregisterUser()- Register new userlogout()- Clear session and logoutuserData()- Fetch user data by token
Development Conventions
Coding Standards
- TypeScript: Strict mode enabled
- Naming:
- Components: PascalCase (
Admin_ScreenDonationStatus) - Files: PascalCase for components (
ScreenDonationStatus.tsx) - Variables: camelCase
- Constants: UPPER_SNAKE_CASE
- Components: PascalCase (
- Path Aliases:
@/*maps to project root - Imports: Group imports by type (components, hooks, services, etc.)
Component Structure
// 1. Imports (grouped)
import { ... } from "@/components";
import { ... } from "@/hooks";
import { ... } from "@/service";
// 2. Types/Interfaces
interface Props { ... }
// 3. Main Component
export function ComponentName() {
// State
// Hooks
// Functions
// Render
}
Testing
- Linting:
bun run lint - No formal test suite configured yet
Git Workflow
- Feature branches:
feature/xxxorfixed-admin/xxx - Commit messages: Clear and descriptive
- Use CHANGE_LOG.md for tracking changes
Key Features
Authentication
- Phone number login with OTP
- User registration
- Terms & Conditions acceptance
- Session persistence with AsyncStorage
Admin Module
- Dashboard: Overview and statistics
- User Access: User management
- Event: Event CRUD with status management
- Voting: Voting management (publish/review/reject)
- Donation: Donation management with categories and transaction tracking
- Collaboration: Collaboration requests
- Investment: Investment management
- Maps: Location-based features
- App Information: Bank and business field management
User Module
- Home: Main dashboard
- Forum: Discussion forums
- Profile: User profile management
- Portfolio: Member portfolio
- Notifications: Push notifications via Firebase
API Configuration
Base URLs
// From app.config.js extra
API_BASE_URL: process.env.API_BASE_URL
BASE_URL: process.env.BASE_URL
DEEP_LINK_URL: process.env.DEEP_LINK_URL
Axios Interceptor
All API calls use apiConfig with automatic token injection:
apiConfig.interceptors.request.use(async (config) => {
const token = await AsyncStorage.getItem("authToken");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
Platform Configuration
iOS
- Bundle ID:
com.anonymous.hipmi-mobile - Build Number: 21
- Google Services: Configured
- Associated Domains:
applinks:cld-dkr-staging-hipmi.wibudev.com - Tablet Support: Enabled
Android
- Package:
com.bip.hipmimobileapp - Version Code: 4
- Google Services: Configured (
google-services.json) - Deep Links: HTTPS intent filters configured
- Edge-to-Edge: Enabled
Web
- Output: Static
- Bundler: Metro
Special Integrations
Firebase
- Authentication
- Push Notifications (FCM)
- Configured for both iOS and Android
Maplibre
- Map integration via
@maplibre/maplibre-react-native - Location permissions configured
Deep Linking
- Scheme:
hipmimobile:// - HTTPS:
cld-dkr-hipmi-stg.wibudev.com - Configured for both platforms
Camera
- Camera and microphone permissions
- QR code generation support
Common Development Tasks
Adding a New Admin Screen
- Create Screen Component (
screens/Admin/Feature/ScreenXXX.tsx):
export function Admin_ScreenXXX() {
const pagination = usePagination({...});
const renderItem = useCallback(...);
const headerComponent = useMemo(...);
return <NewWrapper ... />;
}
- Create Box Component (optional, for custom item rendering):
export default function Admin_BoxXXX({ item }: { item: any }) {
return (
<AdminBasicBox onPress={() => router.push(...)}>
...
</AdminBasicBox>
);
}
- Update API (add pagination if needed):
export async function apiXXX({ page = "1" }: { page?: string }) {
// ...
}
- Create Route File (
app/(application)/admin/feature/xxx.tsx):
import { Admin_ScreenXXX } from "@/screens/Admin/Feature/ScreenXXX";
export default function AdminXXX() {
return <Admin_ScreenXXX />;
}
Updating API Endpoints
- Add function in appropriate service file
- Include
pageparameter for list endpoints - Use
apiConfigaxios instance - Handle errors properly
Troubleshooting
Build Issues
# Clean and rebuild
rm -rf node_modules
bun install
bunx expo prebuild --clean
# iOS specific
cd ios && pod install && cd ..
# Android specific
cd android && ./gradlew clean && cd ..
Cache Issues
# Clear Expo cache
bunx expo start -c
# Clear Metro cache
bunx expo start --clear
Dependency Issues
# Reinstall dependencies
rm -rf node_modules bun.lock
bun install
iOS Maplibre Crash Fix
When using Maplibre MapView on iOS, prevent "Attempt to recycle a mounted view" crash:
- Always render PointAnnotation (not conditional)
- Use opacity for visibility instead of conditional rendering
- Avoid key prop changes that force remounting
// ✅ GOOD: Stable PointAnnotation
<PointAnnotation
coordinate={annotationCoordinate} // Always rendered
...
>
<View style={{ opacity: selectedLocation ? 1 : 0 }}>
<SelectedLocationMarker />
</View>
</PointAnnotation>
// ❌ BAD: Conditional rendering causes crash
{selectedLocation && (
<PointAnnotation coordinate={selectedLocation} ... />
)}
Documentation Files
docs/CHANGE_LOG.md- Change log for recent updatesdocs/hipmi-note.md- Build and deployment notesdocs/prompt-for-qwen-code.md- Development prompts and patterns