FarmConnect is a mobile-first marketplace that connects customers with local farms. Users can browse, reserve, and schedule pickups for fresh produce, while farmers manage inventory, pickup windows, and recurring market events-all in one streamlined platform.
Demo data note: The Supabase migrations include seeded demo farms in
202605050010_seed_demo_farms.sqland login-ready demo accounts in202605111200_seed_full_demo_data.sql. Use these accounts to explore the customer and farmer flows:
Role Password Customer [email protected]DemoPass123!Farmer [email protected]DemoPass123!
View Progress (Features Implemented)
- Demo Landing Page (feature showcase, customer/farmer flow overview, mock phone preview UI)
- Authentication System (email/password login and registration, role selection, email confirmation redirect, forgot password / in-app reset flow)
- Account System (customer profile: display name, phone, location, bio, contact preference, pickup notes; farmer account access control)
- Farm Profile (create, view, edit, and delete farm profiles; owner-only controls)
- Farmer Dashboard (role-gated dashboard with navigation to market and inventory management)
- Market Day Management (full CRUD: create, edit, and delete market days with date/time pickers, location, notes, status filtering)
- Pickup Inventory & Time Slots (farmer-managed per-pickup produce inventory with quantities, pricing, and available time slot windows)
- Farm Stock Management (per-farm produce catalog with stock quantities and pricing; server-side stock decrement on order)
- Produce Browser (browse produce list with Norwegian/English names; detail view with full nutritional info sourced from Matvaretabellen)
- Map (Google Maps integration with searchable farm list sidebar; tap a result to pan the map to that farm)
- Camera (in-app camera capture, image preview, retake, confirm, and upload to Supabase Storage)
- Payment System (Stripe test mode via Supabase Edge Function; order screen with payment sheet, Google Pay support)
- Customer Order History (view past pickup and reservation orders with order items and timestamps)
- Push Notifications (Expo Push API via FCM; notifies customer on payment confirmation and reservation placement)
Supabase Instructions
For email confirmation and password reset links to return to the app, add these redirect URLs in the Supabase dashboard:
farmconnect://auth/confirm
farmconnect://auth/reset-password
The current auth flow includes:
- Registration with email/password
- Profile role selection for customer or farmer
- Login with email/password
- Persisted mobile sessions
- Email confirmation redirect back into the app
- Forgot password email with in-app reset flow
Migrations are in supabase/migrations/ and applied in timestamp order. See supabase/migrations/migrations.md for the full workflow (applying, dry-run, repair).
Stripe Instructions
The app uses Stripe for payment processing via the @stripe/stripe-react-native SDK. Payments are handled through a Supabase Edge Function (create-payment-intent) which creates a Stripe PaymentIntent server-side, keeping the secret key off the client. A second edge function (decrement-stock) handles server-side stock decrement on order completion. Both edge functions are deployed to Supabase. To test a payment, use the following Stripe test card:
| Field | Value |
|---|---|
| Number | 4242 4242 4242 4242 |
| Expiry | 05/55 |
| CVC | 555 |
Generate Produce Data
Run this script to generate produceData.json from Matvaretabellen data and the curated produce list in src/data/produceList.ts.
Each produce item in src/data/produceList.ts must include a foodId that matches the correct Matvaretabellen entry. This foodId is used as the stable reference when generating product data. New items must therefore be matched manually in Matvaretabellen before they are added to the list.
Input: src/data/produceList.ts
Run the script from the project root:
npx tsx scripts/generateProductData.tsThis regenerates: src/data/produceData.json
Camera functionality
src/components/CameraCapture.tsx handles:
- camera permission
- camera preview
- taking a picture
- previewing the image
- retaking the image
- confirming the image
src/lib/image-helpers/image-create.ts handles uploading images
src/lib/image-helpers/image-delete.ts handles deleting images
How to use the camera functionality in another screen:
import CameraCapture from "@/components/CameraCapture";Then create a function that receives the confirmed image URI. This runs after the user presses Use Photo.
const handlePhotoConfirmed = async (uri: string) => {
// Do something with the image URI here.
};If the image should be uploaded immediately, call the upload helper inside that function:
import uploadConfirmedImage from "@/lib/image-helpers/image-create";
const handlePhotoConfirmed = async (uri: string) => {
const { imageUrl, imagePath } = await uploadConfirmedImage(uri);
// imageUrl is the public URL for showing the uploaded image.
// imagePath is the file path used to locate or delete the image in Storage later.
};Finally, render the camera component and pass the function to onPhotoConfirmed.
return <CameraCapture onPhotoConfirmed={handlePhotoConfirmed} />;Push Notifications
Push notifications are delivered via the Expo Push API backed by Firebase Cloud Messaging (FCM). This only works on native Android builds, not Expo Go or web.
On login, the app registers an Expo push token stored in push_tokens. After checkout creates an order, the client calls the notify-payment-success edge function with the user's session JWT, and the function sends the appropriate notification.
google-services.json is required in the project root for native builds and is tracked in this repo. It contains public Firebase client identifiers; service account credentials are managed separately through EAS credentials.
| Event | Recipient | Title | Body |
|---|---|---|---|
| Pickup order placed | Customer | Payment confirmed | Your payment of {amount} NOK was successful. |
| Reservation placed | Customer | Reservation confirmed | Your items are reserved and held for 48 hours. Pay at pickup. |
See CONTRIBUTING for setup instructions and how to contribute.
| Layer | Technology |
|---|---|
| Framework | React Native (Expo) |
| Routing | Expo Router |
| Backend | Supabase (Auth, Database, Edge Functions) |
| Payments | Stripe (@stripe/stripe-react-native) |
| Maps | Google Maps (react-native-maps) |
| Language | TypeScript |
CI runs on every pull request and push to main. Recommended to install Prettier extension. You can run the checks locally:
npm run lint # ESLint
npm run typecheck # TypeScript
npm run format:check # Prettier
npm test # Jest (with coverage)
npm audit # Dependency security auditOr run all checks at once:
./quality-check.shCI also enforces a console.log-free codebase, remove any console.log calls before merging.