A full-stack, multi-device inventory management system built with React Native (Expo) and Node.js/Express, featuring real-time data synchronization across devices with role-based access control.
This application enables teams to manage device inventory collaboratively in real-time. Users can upload devices to lots, track inventory across multiple storage locations, and view shared data across all connected devices and platforms. Data is persisted on a centralized server, ensuring consistency and accessibility for all team members.
Live Status: Running on Ubuntu Server (i5 8th gen, 8GB RAM, 512GB SSD) with Docker containerization.
Recent Updates: See CHANGELOG.md for latest features and improvements.
Developer documentation: See docs/DEVELOPER_GUIDE.md for a full architecture walkthrough, how-to guides, and API reference. See docs/TROUBLESHOOTING.md for common issues and fixes.
- Multi-Device Synchronization: Real-time inventory updates across all connected devices (Android, iOS)
- Role-Based Access Control: Four user roles (Driver, Warehouse, Tech, Manager) with permission-based features
- Lot Management: Upload devices to specific lots with location tracking
- Inventory Search & Filtering: Global search across all inventory items with multiple filter options
- CSV Import/Export: Bulk lot location imports and per-list CSV exports with native sharing
- Device Tracking: Track devices by asset tag, serial number, device type, cosmetic/functional grades, and CPU/RAM specs
- Authentication: JWT-based login with secure password hashing (bcryptjs)
- Dark/Light Theme: Persistent theme preferences with system-level integration and dynamic header styling
- Offline Resilience: Local caching with sync-on-connect capability
- Purchase Tracking:
- Fuel purchase logging with native date picker and required receipt photos
- Management purchase tracking with credit card selection and required receipt images
- Purchase history viewer with CSV export capability
- Role-filtered purchase history (drivers see fuel, managers see management purchases)
- Receipt Image Management: Camera capture and photo library integration with optimized compression
- Version & Attribution: Built-in app version tracking and creator attribution with read-only display
- Improved Navigation: Hamburger side menu with Home, Settings, and Sign Out options; Close button on Settings for quick navigation back
- Smart Server Fallback: Automatic failover between configured server URLs (on-site/remote) during app startup authentication
| Role | Permissions |
|---|---|
| Driver | View home dashboard, submit fuel purchases with receipts, view own fuel purchase history |
| Warehouse | Upload devices to lots, view inventory, access lot tracking |
| Tech | Scan and add inventory items, view and delete scan_add inventory |
| Manager | Full access, upload devices, submit management purchases, view all purchase history, manage users, export data |
- React Native (Expo 54.0): Cross-platform mobile framework
- TypeScript: Type-safe JavaScript for reliability
- @react-navigation: Stack and bottom-tab navigation
- Async Storage: Device-level persistence for offline support
- Expo modules:
expo-file-system- File handlingexpo-document-picker- CSV importexpo-sharing- Native share sheetexpo-vector-icons- UI iconsexpo-constants- App metadata/version displayexpo-image-picker- Camera and photo library access@react-native-community/datetimepicker- Native date picker
- Node.js 20 LTS: Runtime
- Express.js 5.2: REST API framework
- JWT (jsonwebtoken): Stateless authentication
- bcryptjs: Secure password hashing
- CORS: Cross-origin resource sharing
- dotenv: Environment configuration management
- Expo Server SDK: Push notification delivery
- Docker & Docker Compose: Containerization and orchestration
- Ubuntu Server 24.04 LTS: Production OS
- Nginx: Reverse proxy and HTTPS termination
- UFW (Uncomplicated Firewall): Network security
- PM2: Alternative process manager (configured, not primary)
- JSON file storage: Persistence layer (migrate to PostgreSQL for scale)
- AsyncStorage: Mobile device-level caching
Inventory_app/
├── App.tsx # App entry point
├── index.ts # Expo entry
├── server.js # Express backend API
├── src/
│ ├── screens/ # UI screens
│ │ ├── LoginScreen.tsx # Login with company logo and metadata
│ │ ├── HomeScreen.tsx
│ │ ├── UploadItemsScreen.tsx
│ │ ├── InventoryListScreen.tsx
│ │ ├── ItemDetailScreen.tsx
│ │ ├── SettingsScreen.tsx
│ │ ├── DriverPortalScreen.tsx # Fuel purchase form with required receipt image
│ │ ├── ManagementPortalScreen.tsx # Management purchase form with required receipt image
│ │ ├── PurchaseHistoryScreen.tsx # Purchase viewer with CSV export
│ │ └── UserManagementScreen.tsx
│ ├── context/ # State management
│ │ ├── AuthContext.tsx
│ │ └── ThemeContext.tsx # Dark/light mode with dynamic theme switching
│ ├── services/ # API & business logic
│ │ ├── serverApi.ts # Includes purchase submission & history fetching
│ │ └── syncService.ts
│ ├── storage/ # Local persistence
│ │ ├── inventoryStorage.ts
│ │ └── lotUploadStorage.ts
│ ├── navigation/ # Route configuration
│ ├── components/ # Reusable UI components
│ ├── theme/ # Design system (colors, spacing, typography)
│ ├── types/ # TypeScript definitions
│ └── utils/ # Helpers (CSV parsing)
├── docker-compose.yml # Container orchestration
├── Dockerfile # Image definition
├── .env.example # Environment template
├── ecosystem.config.cjs # PM2 configuration
└── docs/
├── PRODUCTION_DEPLOYMENT.md # Deployment guide
├── DEVELOPER_GUIDE.md # Architecture, how-to, API reference
└── TROUBLESHOOTING.md # Common issues and fixes
POST /auth/login- User loginGET /auth/me- Get current user (requires token)
GET /items- Fetch all inventory items (paginated)POST /items- Add new inventory itemGET /lot-items- Fetch lot uploadsPOST /lot-items- Submit device to lot
GET /lots/public- Get lot info (no auth required)GET /lots- Get lot info with locations (requires auth)POST /lots/import- Bulk import lot locations from CSV
POST /fuel-purchases- Log fuel purchase (receipt image required)GET /purchases/fuel- Fetch fuel purchase history (authenticated)POST /management-purchases- Log management purchase (receipt image required)GET /purchases/management- Fetch management purchase history (manager only)
POST /push/register- Register device for push notifications
- Node.js 20+ and npm
- Android 9+ or iOS 12+
- For testing: Expo Go app or built APK/IPA
- For production: Ubuntu Server, Docker 27+
# Clone and install
git clone <repo>
cd Inventory_app
npm install
# Set up environment
cp .env.example .env
# Edit .env with your API URL (examples: http://localhost:3000 or private VPN URL)
# Start development server (Terminal 1)
npm run server
# Start Expo development (Terminal 2)
npm run android # or npm run iosSee docs/PRODUCTION_DEPLOYMENT.md for the comprehensive setup guide including:
- Docker image building
- Environment configuration
- Nginx reverse proxy setup
- HTTPS with Let's Encrypt
- Firewall configuration
- PostgreSQL migration path
Quick start:
# On Ubuntu server
cd ~/Inventory_app
cp .env.example .env
# Edit .env with strong JWT_SECRET
docker compose up -d --build inventory-server- Mobile App captures device information (asset tag, device type, lot number)
- App sends to Express Server via authenticated REST API
- Server persists to shared JSON files (or PostgreSQL)
- Other devices fetch latest data via
GET /items,GET /lot-items - Conflict resolution: Latest timestamp wins; no offline-first merging yet
- User logs in with username/password
- Server validates credentials (bcryptjs hash comparison)
- On success, JWT token returned and stored in AsyncStorage
- Token included in all subsequent API requests
- Role verified on backend for permission checks
- Automatic server fallback: On app boot, attempts authentication across configured server URLs (saved, onsite, remote, default) until successful connection is established and persists the working URL
- Side Menu: Hamburger button (top-left) opens animated slide-in panel showing user name, role, and quick navigation
- Home: Navigate back to home screen
- Settings: Access app configuration and dual-server URL profiles
- Sign Out: Exit and return to login
- Settings Screen: Close button (top-right X icon) for quick navigation back
- Animations: Smooth 220ms menu slide-in and 180ms slide-out for professional feel
- On app launch, fetch all inventory and lot items from server
- On device upload, immediate sync to server
- Pending uploads queued if offline (stored locally)
- CSV imports bulk-insert via
/lots/importendpoint
-
Fuel Purchases (Drivers):
- Form with native date picker for purchase date
- Amount field and "Purchased At" location tracking
- Receipt photo is mandatory before submit (camera or photo library)
- Optional notes field
- Image quality automatically compressed (camera: 50%, library: 30%) to fit 2MB server limit
- Submit with user attribution
-
Management Purchases (Managers):
- Similar form with credit card selection (Chase Business or Charity Charge)
- Receipt image is mandatory before submit
- Uses same compression optimization
- Track spending by card type
-
Purchase History Viewer:
- Accessible from Driver and Management portals
- Dual-tab interface (Fuel purchases visible to all, Management purchases managers-only)
- Swipe-to-refresh for latest data
- Tap receipt to view full-size image in modal
- CSV export with timestamp, amount, submitted by, card type (if applicable)
- Exported CSVs shareable via native share sheet
The server creates users.json with four default accounts on first run (only if the file does not exist):
| Username | Password | Role |
|---|---|---|
manager1 |
password123 |
manager |
driver1 |
password123 |
driver |
warehouse1 |
password123 |
warehouse |
tech1 |
password123 |
tech |
Set a strong JWT_SECRET, rotate all default credentials, and create named user accounts before going to production.
- React Native fundamentals: Components, hooks, navigation, bottom tabs
- Expo ecosystem: Build streamlined mobile apps without native code complexity
- Expo EAS: Cloud-based app building for Android/iOS
- Cross-platform UI: Responsive design adapting to different screen sizes and orientations
- AsyncStorage: Client-side persistence and state hydration
- Native modules: File I/O, document picker, sharing via Expo APIs
- Deployment: Building APK/IPA files for distribution
- Express.js API design: RESTful endpoints, middleware, error handling
- Authentication & Authorization: JWT tokens, role-based access control (RBAC), middleware-based permission checks
- Data persistence: File-based JSON storage with atomic writes, migration path to SQL databases
- API security: CORS, input validation, password hashing
- Push notifications: Server-side push notification delivery via Expo
- Client-server communication: JSON APIs, fetch requests, error handling
- State management: Context API for global auth/theme state
- Type safety: TypeScript for both frontend and backend
- Environment management: dotenv for configuration across environments
- Error handling: Graceful failures, retry logic, user feedback
- Docker: Containerization, Dockerfile optimization, multi-stage builds
- Docker Compose: Service orchestration, volume management, networking
- Linux/Ubuntu: Server administration, user management, file permissions
- Firewalls: UFW rules for port management and security
- Reverse proxying: Nginx configuration, port forwarding, HTTPS termination (ready for setup)
- Process management: PM2 for long-running processes (alternative to Docker)
- Network configuration: Static IPs, firewall rules, port management
- Scalability thinking: Identified JSON→PostgreSQL migration path; understood sharding/replication concepts
- Real-time sync: Designed multi-device synchronization without conflict resolution framework
- Role-based permissions: Implemented permission system; understood privilege escalation risks
- API versioning: Structured endpoints for future backward compatibility
- CSV handling: Parsing, validation, and bulk import logic
- Version control: Git workflow, .gitignore, code commits
- Testing: TypeScript compilation, runtime validation
- Documentation: Code comments, deployment guides, README
- Clean code: Refactoring data flow, removing unused files, consistent naming
- Debugging: Docker logs, browser DevTools, server-side error traces
| Challenge | Solution |
|---|---|
| Mobile apps losing connection to local dev server | Switched to server-backed API with private VPN or secure remote endpoint |
| Data inconsistency across devices | Centralized data on server; server as source of truth |
| CSV parsing complexity | Built custom parser handling both tab and comma delimiters |
| Expo Go's QR code dependency | Built standalone APK via EAS for persistent installation |
| Docker image not picking up code changes | Added --build flag to rebuild on deploy |
| Lot names not displaying | Fixed readLotInfo() to return locations array |
| File transfer time (scp including node_modules) | Used tar with .exclude flags for fast transfer |
| Port listening on localhost only | Ensured Docker containers expose ports to 0.0.0.0 |
- PostgreSQL migration for production scalability
- Conflict-free replicated data types (CRDTs) for offline-first sync
- Photo capture for device conditions
- QR code generation for devices
- Email notifications to managers
- Dashboard analytics (trends, device lifespan)
- Multi-warehouse support
- Advanced search (date ranges, condition filters)
- Audit logs for compliance
- Mobile app auto-updates
- Machine learning for device recommendations
- Integration with asset tracking hardware (RFID/Bluetooth tags)
- White-label version for other organizations
- Mobile app for managers with full admin interface
Once initial setup is complete, deploy new code with a single command:
From your local machine:
git add -A
git commit -m "your update message"
git push origin mainOn the Ubuntu server:
ssh <your-user>@<YOUR_SERVER_IP>
deployThe deploy command automatically:
- Pulls latest code from GitHub
mainbranch - Preserves
.envconfiguration - Rebuilds Docker image
- Restarts the inventory-server container
- Performs health check
- On-Site (local network): your LAN IP, e.g.
http://192.168.1.x:3000 - Remote (VPN): your VPN/remote IP, e.g.
http://YOUR_VPN_IP:3000
Configure both URLs in app.json under expo.extra before building.
The app includes automatic server fallback at boot — if one URL fails during authentication, it tries configured alternatives automatically.
- TypeScript compiles without errors
- Server runs locally on port 3000
- Docker image builds successfully
- Docker Compose orchestrates container
- Firewall allows HTTP/HTTPS traffic
- Mobile app connects to server IP
- CSV import works end-to-end
- Multi-device sync confirmed
- Users can login and see role-based UI
- Side menu navigation with Home, Settings, Sign Out
- Close button on Settings screen
- GitHub SSH deploy key configured
- One-command
deployshortcut set up - Server URL fallback for authentication
- TLS/HTTPS configured with Nginx
- Monitoring set up (healthcheck + auto-restart)
- Backup strategy implemented (nightly cron + retention)
Detailed deployment commands and operations runbook are in docs/PRODUCTION_DEPLOYMENT.md.
- Full-stack ownership: Building both frontend and backend gives end-to-end visibility into data flow and performance
- DevOps is crucial: Docker, networking, and firewall knowledge bridges dev environment and production
- API design matters: Clear, consistent endpoints make frontend development smooth and future-proof changes
- Type safety saves debugging time: TypeScript caught errors that would be runtime bugs in JavaScript
- User role modeling: Early permission system design prevents security rework later
- Documentation pays off: Deployment guide made recreation on new hardware straightforward
- Mobile app distribution: Building standalone APKs via EAS beats QR scanning for real users
- Server as source of truth: Centralizing data eliminates sync conflicts and simplifies logic
MIT
Refer to docs/PRODUCTION_DEPLOYMENT.md for deployment support or explore the source code in src/ for implementation details.