Skip to content

Repository files navigation

πŸ”§ Garagena β€” Auto Repair Service Hub

A mobile application connecting vehicle owners with local garages and automotive marketplaces in Morocco.

Built with React Native (Expo) + Node.js/Express + PostgreSQL/PostGIS following a strict Feature-Based Redux-Saga architecture.


πŸ“‹ Table of Contents

  1. Tech Stack
  2. Project Structure
  3. Getting Started
  4. Architecture Rules
  5. API Reference
  6. Database Schema
  7. Environment Variables
  8. Troubleshooting

Tech Stack

Layer Technology
Mobile App React Native 0.73 + Expo SDK 50
State Management Redux 5 + Redux-Saga
Styling React Native StyleSheet
API Client Axios
Backend Node.js + Express
Database PostgreSQL 17 + PostGIS 3.5
Auth JWT (jsonwebtoken + bcrypt)
Infrastructure Docker + Docker Compose

Project Structure

mecanic_hub3/
β”œβ”€β”€ docker-compose.yml        # Database service (PostgreSQL + PostGIS)
β”œβ”€β”€ init.sql                  # Full DB schema + seed data (auto-run by Docker)
β”‚
β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ .env                  # ⚠️ NOT committed β€” see Environment Variables below
β”‚   β”œβ”€β”€ server.js             # Express app entry point
β”‚   β”œβ”€β”€ package.json
β”‚   β”œβ”€β”€ config/
β”‚   β”‚   └── database.js       # PostgreSQL connection pool
β”‚   β”œβ”€β”€ middleware/
β”‚   β”‚   └── auth.js           # JWT verification middleware
β”‚   └── routes/
β”‚       β”œβ”€β”€ auth.js           # POST /auth/login, /auth/register
β”‚       β”œβ”€β”€ garages.js        # GET /garages/search, /garages/:id
β”‚       β”œβ”€β”€ marketplace.js    # GET/POST /marketplace/posts
β”‚       β”œβ”€β”€ profile.js        # GET /profile/client, /profile/garage
β”‚       └── dashboard.js      # GET /dashboard/client, /dashboard/garage
β”‚
└── src/
    β”œβ”€β”€ features/             # Feature modules (see Architecture Rules)
    β”‚   β”œβ”€β”€ Auth/
    β”‚   β”œβ”€β”€ GarageDiscovery/
    β”‚   β”œβ”€β”€ Marketplace/
    β”‚   └── Profile/
    β”‚       β”œβ”€β”€ ClientProfile/
    β”‚       └── GarageProfile/
    β”œβ”€β”€ components/           # Reusable UI components
    β”‚   β”œβ”€β”€ AppButton.js
    β”‚   β”œβ”€β”€ AppCard.js
    β”‚   β”œβ”€β”€ AppInput.js
    β”‚   β”œβ”€β”€ GarageCard.js
    β”‚   └── CustomTabNavigator.js
    β”œβ”€β”€ navigation/
    β”‚   └── AppNavigator.js   # Root navigator (Auth vs Main flow)
    β”œβ”€β”€ services/
    β”‚   └── api.js            # Axios instance β€” update API_URL for your machine
    └── store/
        β”œβ”€β”€ index.js
        β”œβ”€β”€ rootReducer.js
        └── rootSaga.js

Getting Started

Prerequisites

  • Node.js v18+
  • Docker Desktop (running)
  • Android Studio (for emulator) or Expo Go app on a physical device

Step 1 β€” Start the Database

# From project root
docker-compose up -d

# Verify it's healthy
docker ps
# Expected: mecanic_hub_db   Up (healthy)

# Verify seed data
docker exec -it mecanic_hub_db psql -U postgres -d garagena -c "SELECT name, city FROM garages;"

The database schema and seed data from init.sql run automatically on the first start.


Step 2 β€” Configure & Start the Backend

cd backend
npm install

Create backend/.env (or verify it exists):

PORT=3000
NODE_ENV=development

DB_HOST=localhost
DB_PORT=5432
DB_NAME=garagena
DB_USER=postgres
DB_PASSWORD=0000

JWT_SECRET=change_this_in_production
JWT_EXPIRES_IN=7d
node server.js
# Expected:
# βœ… Database connected successfully
# πŸš€ Server running on port 3000

Test the backend:

# Should return garages in Rabat
curl "http://localhost:3000/api/garages/search?city=Rabat"

Step 3 β€” Configure & Start the Frontend

Find your machine's local IP:

# Windows PowerShell
ipconfig
# Look for "IPv4 Address" under your active network adapter

Update the API URL in src/services/api.js:

const API_URL = 'http://YOUR_LOCAL_IP:3000/api';
// Example: 'http://192.168.1.50:3000/api'
// ⚠️ Use 10.0.2.2 instead of localhost for Android Emulator
# From project root
npm install
npx expo start --lan
  • Press a β†’ Android Emulator
  • Scan QR code with Expo Go β†’ Physical device (must be on same WiFi)

Step 4 β€” Login with Seed Data

Role Email Password
Client youssef@gmail.com secret
Client fatima@gmail.com secret
Garage owner garage1@gmail.com secret
Admin admin@garagena.ma secret

Architecture Rules

Every feature must follow this exact structure. No exceptions.

src/features/[FeatureName]/
β”œβ”€β”€ actionTypes/index.js    # String constants (e.g. FETCH_GARAGES_REQUEST)
β”œβ”€β”€ actions/index.js        # Action creator functions
β”œβ”€β”€ initialState/index.js   # Default Redux state shape
β”œβ”€β”€ reducer/index.js        # Pure reducer function
β”œβ”€β”€ sagas/index.js          # All API calls (side effects)
β”œβ”€β”€ selectors/index.js      # Reselect memoized selectors
β”œβ”€β”€ useCases/
β”‚   β”œβ”€β”€ service.js          # Business logic (custom React hook)
β”‚   └── main.js             # UI component (renders only)
└── index.js                # Public exports

Golden Rules

Rule Detail
βœ… API calls Only in sagas/index.js via call(api.get, ...)
βœ… Business logic Only in useCases/service.js
βœ… UI / JSX Only in useCases/main.js
βœ… State reads Only via selectors/ using useSelector
βœ… State writes Only via actions/ dispatched through Redux
❌ Never Direct axios calls inside components
❌ Never useState for server data
❌ Never Business logic inside main.js

Adding a New Feature β€” Checklist

  • Create folder src/features/NewFeature/
  • Define actionTypes/index.js
  • Create actions/index.js
  • Define initialState/index.js
  • Create reducer/index.js
  • Create sagas/index.js (add to store/rootSaga.js)
  • Create selectors/index.js
  • Create useCases/service.js
  • Create useCases/main.js
  • Export from index.js
  • Add reducer to store/rootReducer.js
  • Add to navigation in AppNavigator.js if needed

API Reference

Authentication

Method Endpoint Auth Description
POST /api/auth/register No Register new user
POST /api/auth/login No Login, returns JWT
GET /api/auth/me Yes Get current user

Register body:

{ "email": "user@example.com", "password": "Test1234!", "name": "John", "role": "client" }

Garages

Method Endpoint Auth Description
GET /api/garages/search No Search garages
GET /api/garages/:id No Get garage detail
POST /api/garages/:id/interaction Yes Track a click

Search query params:

Param Type Example Description
city string Rabat Filter by city
category string SERVICE SERVICE, RETAIL, BOTH
lat + lng float 34.01,-6.83 GPS center point
radius int 10 Radius in km (default: 50)
sortBy string rating rating or distance
minRating float 4.0 Minimum rating filter

Marketplace

Method Endpoint Auth Description
GET /api/marketplace/posts No List items
POST /api/marketplace/posts Yes Create listing
GET /api/marketplace/posts/:id No Item detail
PUT /api/marketplace/posts/:id Yes Update listing
DELETE /api/marketplace/posts/:id Yes Delete listing

Profile

Method Endpoint Auth Description
GET /api/profile/client Yes Client profile
PUT /api/profile/client Yes Update client profile
GET /api/profile/client/vehicles Yes List vehicles
POST /api/profile/client/vehicles Yes Add vehicle
GET /api/profile/client/favorites Yes Favorite garages
POST /api/profile/client/favorites/:id Yes Add favorite
GET /api/profile/garage Yes Garage profile
PUT /api/profile/garage Yes Update garage profile

Database Schema

See init.sql for the complete schema. Summary:

users
 └── garages         (one user β†’ one garage for garage role)
 └── vehicles        (one user β†’ many vehicles)
 └── favorites       (user ↔ garage junction)
 └── notifications   (one user β†’ many notifications)
 └── messages        (user β†’ user)

garages
 └── appointments    (client user + garage + vehicle)
 └── posts           (reviews: user + garage + rating)
 └── marketplace_items
 └── interactions    (analytics: whatsapp_click, call_click, etc.)
 └── garage_certifications

Garages use PostGIS GEOMETRY(Point, 4326) for GPS-based radius search.


Environment Variables

backend/.env β€” required, never commit

PORT=3000
NODE_ENV=development

DB_HOST=localhost
DB_PORT=5432
DB_NAME=garagena
DB_USER=postgres
DB_PASSWORD=0000

JWT_SECRET=change_this_in_production
JWT_EXPIRES_IN=7d

src/services/api.js β€” update per machine

const API_URL = 'http://<YOUR_LOCAL_IP>:3000/api';

For Android Emulator: use 10.0.2.2 instead of localhost


Troubleshooting

Problem Solution
Error: connect ECONNREFUSED ::1:5432 Docker DB not running: docker-compose up -d
Network Error on device Update API_URL in api.js with your PC's IP. Ensure same WiFi.
Port 5432 already in use Stop local Postgres: net stop postgresql-x64-17
Port 3000 already in use netstat -ano | findstr :3000 then kill the PID
Blank screen on login Check Metro console for JS errors. Run npx expo start --clear
GPS not working on emulator Set a manual location in Android Emulator's "..." β†’ Location tab
Cannot read property 'latitude' of null City search does not require GPS β€” this is handled automatically
DB schema out of date Drop and recreate: docker-compose down -v && docker-compose up -d

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages