A React Native (Expo) application for organising and booking activities. Built to run on iOS, Android, and Web.
- Create Activities: Define activities with name, description, photos, location (city and exact address), date range, and pricing
- Manage Slots: Configure available time slots for each activity (e.g., 5 slots per weekday from 9 AM)
- Slot Availability: Toggle slot availability when you're not available on certain days
- View Bookings: See all bookings for your activities with user details
- Revenue Tracking: Track total bookings and revenue for each activity
- Browse Activities: Search and filter activities by city, date range, and keywords
- View Details: See full activity information including location, pricing, and available slots
- Book Slots: Select and book available time slots with cash-on-arrival payment
- My Bookings: View all bookings - upcoming, past, and cancelled
- Cancel Bookings: Cancel upcoming bookings if plans change
- SQLite Database: Local database for offline capability
- Configurable Database Layer: Abstract interface allows easy swap to PostgreSQL, MySQL, etc.
- Cross-Platform: Runs on iOS, Android, and Web via Expo
- Responsive UI: Clean, modern design that works on all screen sizes
- Node.js 18+
- npm or yarn
- Expo CLI (optional but recommended)
- iOS Simulator (for iOS development on Mac)
- Android Studio (for Android development)
# Navigate to the project directory
cd join-me
# Install dependencies
npm install
# Start the development server
npx expo start# Run on web browser
npm run web
# Run on iOS simulator
npm run ios
# Run on Android emulator
npm run androidjoin-me/
├── App.js # Main app entry point
├── src/
│ ├── components/ # Reusable UI components
│ │ ├── Button.js
│ │ ├── Input.js
│ │ ├── ActivityCard.js
│ │ ├── BookingCard.js
│ │ └── SlotCard.js
│ ├── context/ # React Context providers
│ │ └── AuthContext.js # Authentication state management
│ ├── database/ # Database layer
│ │ ├── DatabaseInterface.js # Abstract interface
│ │ ├── SQLiteDatabase.js # SQLite implementation
│ │ └── index.js # Database export
│ ├── navigation/ # Navigation configuration
│ │ ├── AppNavigator.js
│ │ ├── AuthNavigator.js
│ │ ├── UserNavigator.js
│ │ └── OrganiserNavigator.js
│ ├── screens/ # App screens
│ │ ├── auth/
│ │ │ ├── LoginScreen.js
│ │ │ └── RegisterScreen.js
│ │ ├── organiser/
│ │ │ ├── OrganiserDashboard.js
│ │ │ ├── CreateActivityScreen.js
│ │ │ └── ManageActivityScreen.js
│ │ ├── user/
│ │ │ ├── ExploreScreen.js
│ │ │ ├── ActivityDetailsScreen.js
│ │ │ └── MyBookingsScreen.js
│ │ └── common/
│ │ └── ProfileScreen.js
│ └── utils/ # Utility functions
│ └── dateUtils.js
├── assets/ # Images and static assets
└── app.json # Expo configuration
| Column | Type | Description |
|---|---|---|
| id | TEXT | Primary key (UUID) |
| TEXT | Unique email address | |
| password | TEXT | User password (should be hashed in production) |
| name | TEXT | Full name |
| userType | TEXT | 'organiser' or 'user' |
| phone | TEXT | Phone number (optional) |
| city | TEXT | City (optional) |
| createdAt | TEXT | ISO timestamp |
| updatedAt | TEXT | ISO timestamp |
| Column | Type | Description |
|---|---|---|
| id | TEXT | Primary key (UUID) |
| organiserId | TEXT | Foreign key to users |
| name | TEXT | Activity name |
| description | TEXT | Activity description |
| photoUri | TEXT | Photo URI (optional) |
| city | TEXT | City-level location |
| exactLocation | TEXT | Full address |
| startDate | TEXT | Start date (YYYY-MM-DD) |
| endDate | TEXT | End date (YYYY-MM-DD) |
| pricePerSlot | REAL | Price per booking |
| maxSlotsPerDay | INTEGER | Maximum slots per day |
| weekdaysOnly | INTEGER | 1 for weekdays only |
| Column | Type | Description |
|---|---|---|
| id | TEXT | Primary key (UUID) |
| activityId | TEXT | Foreign key to activities |
| date | TEXT | Slot date (YYYY-MM-DD) |
| startTime | TEXT | Start time (HH:MM) |
| endTime | TEXT | End time (HH:MM) |
| isAvailable | INTEGER | 1 if available |
| Column | Type | Description |
|---|---|---|
| id | TEXT | Primary key (UUID) |
| userId | TEXT | Foreign key to users |
| slotId | TEXT | Foreign key to slots |
| activityId | TEXT | Foreign key to activities |
| status | TEXT | 'confirmed', 'cancelled', or 'completed' |
| paymentMethod | TEXT | 'cash_on_counter' |
| paymentStatus | TEXT | 'pending', 'paid', or 'refunded' |
| totalAmount | REAL | Total booking amount |
The app uses a database interface pattern. To switch from SQLite to another database:
- Create a new implementation file (e.g.,
PostgresDatabase.js) that extendsDatabaseInterface - Implement all required methods
- Update
src/database/index.jsto import and export your new implementation
Example:
// src/database/index.js
import database from './PostgresDatabase'; // Switch implementation
export default database;Currently, the app supports "Cash on Counter" payment only. To add payment gateways:
- Create a payment service in
src/services/ - Integrate with Stripe, PayPal, or other providers
- Update the booking flow in
ActivityDetailsScreen.js
To connect to a backend API instead of local SQLite:
- Create
src/services/ApiService.jswith HTTP client (axios/fetch) - Create
src/database/ApiDatabase.jsimplementingDatabaseInterface - Map API endpoints to database methods
- Switch the import in
src/database/index.js
MIT License - Feel free to use this project for learning or commercial purposes.