Skip to content

Instantly share code, notes, and snippets.

@shopglobal
Created August 24, 2024 23:00
Show Gist options
  • Save shopglobal/f288b730dc07ea069d5c3cd5e1548e31 to your computer and use it in GitHub Desktop.
Save shopglobal/f288b730dc07ea069d5c3cd5e1548e31 to your computer and use it in GitHub Desktop.
Fishing charter

Creating an app to connect fishing charter boats with fishermen and people who want to rent fishing boats involves several key features and components. Here’s a high-level overview of how you might approach this:

1. Define Features

For Charter Boat Owners:

  • Profile Creation: Register and create profiles with boat details, pricing, and availability.
  • Booking Management: Manage bookings and schedules.
  • Reviews and Ratings: Receive and respond to reviews from customers.

For Fishermen/Boat Renters:

  • Search and Filter: Search for available charter boats based on location, type, and availability.
  • Booking: Book charters and pay securely through the app.
  • Reviews and Ratings: Leave reviews and ratings for charter boats.

Common Features:

  • User Authentication: Secure login and registration.
  • Notifications: Alerts for booking confirmations, cancellations, and new messages.
  • Payment Integration: Secure payment processing.
  • Messaging: In-app messaging between charter boat owners and renters.

2. Technology Stack

  • Frontend: React Native for cross-platform mobile app development.
  • Backend: Node.js with Express.js or PHP for handling API requests.
  • Database: MongoDB or MySQL for storing user data, bookings, and reviews.
  • Payment Integration: Stripe or PayPal for handling transactions.

3. Development Steps

Backend with Node.js and Express.js

a. Initialize Project:

mkdir fishing-charter-backend
cd fishing-charter-backend
npm init -y
npm install express mongoose body-parser cors

b. Create server.js:

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');

const app = express();
const port = 5000;

app.use(bodyParser.json());
app.use(cors());

// Connect to MongoDB
mongoose.connect('mongodb://localhost/fishing_charter', { useNewUrlParser: true, useUnifiedTopology: true });

// Define schemas and models
const BoatSchema = new mongoose.Schema({
    name: String,
    location: String,
    price: Number,
    availability: Boolean,
    description: String,
});

const Boat = mongoose.model('Boat', BoatSchema);

// Routes
app.get('/boats', async (req, res) => {
    const boats = await Boat.find();
    res.json(boats);
});

app.post('/boats', async (req, res) => {
    const newBoat = new Boat(req.body);
    await newBoat.save();
    res.json(newBoat);
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
});

Frontend with React Native

a. Initialize Project:

npx react-native init FishingCharterApp
cd FishingCharterApp
npm install axios react-navigation react-navigation-stack

b. Create Basic UI Components

App.js:

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
import BookingScreen from './screens/BookingScreen';

const Stack = createStackNavigator();

const App = () => {
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName="Home">
                <Stack.Screen name="Home" component={HomeScreen} />
                <Stack.Screen name="Booking" component={BookingScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
};

export default App;

HomeScreen.js:

import React, { useState, useEffect } from 'react';
import { View, Text, Button, FlatList, StyleSheet } from 'react-native';
import axios from 'axios';

const HomeScreen = ({ navigation }) => {
    const [boats, setBoats] = useState([]);

    useEffect(() => {
        const fetchBoats = async () => {
            try {
                const response = await axios.get('http://localhost:5000/boats');
                setBoats(response.data);
            } catch (error) {
                console.error('Error fetching boats', error);
            }
        };

        fetchBoats();
    }, []);

    return (
        <View style={styles.container}>
            <FlatList
                data={boats}
                keyExtractor={(item) => item._id}
                renderItem={({ item }) => (
                    <View style={styles.item}>
                        <Text style={styles.title}>{item.name}</Text>
                        <Text>Location: {item.location}</Text>
                        <Text>Price: ${item.price}</Text>
                        <Button
                            title="Book Now"
                            onPress={() => navigation.navigate('Booking', { boatId: item._id })}
                        />
                    </View>
                )}
            />
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        padding: 16,
        backgroundColor: '#f8f8f8',
    },
    item: {
        padding: 16,
        borderBottomWidth: 1,
        borderBottomColor: '#ccc',
    },
    title: {
        fontSize: 18,
        fontWeight: 'bold',
    },
});

export default HomeScreen;

BookingScreen.js:

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

const BookingScreen = ({ route }) => {
    const { boatId } = route.params;

    return (
        <View style={styles.container}>
            <Text>Booking details for boat ID: {boatId}</Text>
            {/* Add booking form and functionality here */}
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        padding: 16,
        backgroundColor: '#f8f8f8',
    },
});

export default BookingScreen;

4. Testing and Deployment

  • Local Testing: Ensure that both the backend and the React Native app are running. The app should fetch and display boat data from the PHP API.
  • Deployment: Host the backend on a cloud service (e.g., AWS, Heroku) and configure the app to use the public API endpoint. Publish the mobile app to app stores.

5. Additional Considerations

  • User Authentication: Implement login and registration features for both charter boat owners and renters.
  • Advanced Features: Add map integration, real-time availability, push notifications, and more.

This basic outline should help you get started on developing your fishing charter app. Adjust and expand based on your specific requirements and feedback.

Certainly! Let's delve deeper into creating a comprehensive app for connecting fishing charter boats with fishermen and boat renters. We’ll break this down into more detailed components for the backend and frontend, as well as additional considerations for deployment and scaling.

1. Detailed Backend Development

a. Project Setup

i. Directory Structure:

fishing-charter-backend/
│
├── models/
│   ├── Boat.js
│   └── User.js
│
├── routes/
│   ├── boatRoutes.js
│   └── userRoutes.js
│
├── server.js
├── .env
└── config/
    └── db.js

ii. Initialize Project:

mkdir fishing-charter-backend
cd fishing-charter-backend
npm init -y
npm install express mongoose body-parser cors dotenv

iii. server.js:

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
require('dotenv').config();

const app = express();
const port = process.env.PORT || 5000;

// Middleware
app.use(bodyParser.json());
app.use(cors());

// Database connection
const dbURI = process.env.MONGO_URI || 'mongodb://localhost/fishing_charter';
mongoose.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true });

// Routes
const boatRoutes = require('./routes/boatRoutes');
const userRoutes = require('./routes/userRoutes');

app.use('/api/boats', boatRoutes);
app.use('/api/users', userRoutes);

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
});

iv. config/db.js:

const mongoose = require('mongoose');

const connectDB = async () => {
    try {
        await mongoose.connect(process.env.MONGO_URI, {
            useNewUrlParser: true,
            useUnifiedTopology: true,
        });
        console.log('MongoDB connected');
    } catch (err) {
        console.error(err.message);
        process.exit(1);
    }
};

module.exports = connectDB;

v. models/Boat.js:

const mongoose = require('mongoose');

const BoatSchema = new mongoose.Schema({
    name: { type: String, required: true },
    location: { type: String, required: true },
    price: { type: Number, required: true },
    availability: { type: Boolean, required: true },
    description: { type: String, required: true },
    owner: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
});

module.exports = mongoose.model('Boat', BoatSchema);

vi. models/User.js:

const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({
    name: { type: String, required: true },
    email: { type: String, required: true, unique: true },
    password: { type: String, required: true },
    role: { type: String, enum: ['owner', 'renter'], required: true },
});

module.exports = mongoose.model('User', UserSchema);

vii. routes/boatRoutes.js:

const express = require('express');
const router = express.Router();
const Boat = require('../models/Boat');

// Get all boats
router.get('/', async (req, res) => {
    try {
        const boats = await Boat.find();
        res.json(boats);
    } catch (err) {
        res.status(500).json({ message: err.message });
    }
});

// Add new boat
router.post('/', async (req, res) => {
    const boat = new Boat(req.body);
    try {
        const newBoat = await boat.save();
        res.status(201).json(newBoat);
    } catch (err) {
        res.status(400).json({ message: err.message });
    }
});

module.exports = router;

viii. routes/userRoutes.js:

const express = require('express');
const router = express.Router();
const User = require('../models/User');

// Register new user
router.post('/register', async (req, res) => {
    const user = new User(req.body);
    try {
        const newUser = await user.save();
        res.status(201).json(newUser);
    } catch (err) {
        res.status(400).json({ message: err.message });
    }
});

// Login user
router.post('/login', async (req, res) => {
    const { email, password } = req.body;
    try {
        const user = await User.findOne({ email });
        if (!user || user.password !== password) {
            return res.status(400).json({ message: 'Invalid credentials' });
        }
        res.json({ message: 'Login successful' });
    } catch (err) {
        res.status(500).json({ message: err.message });
    }
});

module.exports = router;

2. Detailed Frontend Development

a. Directory Structure:

FishingCharterApp/
│
├── screens/
│   ├── HomeScreen.js
│   ├── BookingScreen.js
│   └── ProfileScreen.js
│
├── components/
│   ├── BoatCard.js
│   └── BookingForm.js
│
├── App.js
└── navigation/
    └── AppNavigator.js

b. AppNavigator.js:

import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from '../screens/HomeScreen';
import BookingScreen from '../screens/BookingScreen';
import ProfileScreen from '../screens/ProfileScreen';

const Stack = createStackNavigator();

const AppNavigator = () => {
    return (
        <Stack.Navigator initialRouteName="Home">
            <Stack.Screen name="Home" component={HomeScreen} />
            <Stack.Screen name="Booking" component={BookingScreen} />
            <Stack.Screen name="Profile" component={ProfileScreen} />
        </Stack.Navigator>
    );
};

export default AppNavigator;

c. HomeScreen.js:

import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
import axios from 'axios';
import BoatCard from '../components/BoatCard';

const HomeScreen = ({ navigation }) => {
    const [boats, setBoats] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        const fetchBoats = async () => {
            try {
                const response = await axios.get('http://localhost:5000/api/boats');
                setBoats(response.data);
                setLoading(false);
            } catch (error) {
                console.error('Error fetching boats', error);
                setLoading(false);
            }
        };

        fetchBoats();
    }, []);

    if (loading) {
        return <Text>Loading...</Text>;
    }

    return (
        <View style={styles.container}>
            <FlatList
                data={boats}
                keyExtractor={(item) => item._id}
                renderItem={({ item }) => (
                    <BoatCard
                        boat={item}
                        onPress={() => navigation.navigate('Booking', { boatId: item._id })}
                    />
                )}
            />
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        padding: 16,
        backgroundColor: '#f8f8f8',
    },
});

export default HomeScreen;

d. BoatCard.js:

import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';

const BoatCard = ({ boat, onPress }) => {
    return (
        <TouchableOpacity style={styles.card} onPress={onPress}>
            <Text style={styles.title}>{boat.name}</Text>
            <Text>Location: {boat.location}</Text>
            <Text>Price: ${boat.price}</Text>
        </TouchableOpacity>
    );
};

const styles = StyleSheet.create({
    card: {
        padding: 16,
        borderBottomWidth: 1,
        borderBottomColor: '#ccc',
        backgroundColor: '#fff',
    },
    title: {
        fontSize: 18,
        fontWeight: 'bold',
    },
});

export default BoatCard;

e. BookingScreen.js:

import React, { useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import BookingForm from '../components/BookingForm';

const BookingScreen = ({ route }) => {
    const { boatId } = route.params;

    return (
        <View style={styles.container}>
            <Text>Booking details for boat ID: {boatId}</Text>
            <BookingForm boatId={boatId} />
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        padding: 16,
        backgroundColor: '#f8f8f8',
    },
});

export default BookingScreen;

f. BookingForm.js:

import React, { useState } from 'react';
import { View, TextInput, Button, StyleSheet } from 'react-native';
import axios from 'axios';

const BookingForm = ({ boatId }) => {
    const [name, setName] = useState('');
    const [date, setDate] = useState('');

    const handleBooking = async () => {
        try {
            await axios.post('http://localhost:5000/api/bookings', { boatId, name, date });
            alert('Booking successful');
        } catch (error) {
            console.error('Error booking boat', error);
        }
    };

    return (
        <View style={styles.form}>
            <TextInput
                style={styles.input}
                placeholder="Your Name"
                value={name}
                onChangeText={setName}
            />
            <TextInput
                style={styles.input}
                placeholder="Date (YYYY-MM-DD)"
                value={date}
                onChangeText={setDate}
            />
            <Button title="Book Now" onPress={handleBooking} />
        </View>
    );
};

const styles = StyleSheet.create({
    form: {
        marginTop: 20,
    },
    input: {
        height: 40,
        borderColor: '#ddd',
        borderBottomWidth: 1,
        marginBottom: 10,
        paddingHorizontal: 8,
    },
});

export default BookingForm;

3. Additional Features

a. User Authentication

  • Implement JWT authentication for secure access.
  • Add login and registration screens.
  • Use libraries like jsonwebtoken on the backend and @react-native-community/async-storage on the frontend to handle tokens.

b. Map Integration

  • Use a map API like Google Maps or Mapbox to show boat locations.
  • Add map view components to your HomeScreen.

c. Notifications

  • Integrate push notifications using services like Firebase Cloud Messaging (FCM) or OneSignal.

d. Payment Integration

  • Use Stripe or PayPal SDKs for handling payments.
  • Implement a secure payment flow for booking confirmations.

e. Admin Dashboard

  • Create an admin panel (possibly as a web app) for managing boat listings, user accounts, and booking statuses.

f. Deployment

  • Backend: Deploy using cloud services like Heroku, AWS, or DigitalOcean.
  • Frontend: Use Expo for React Native to build and deploy apps to Google Play Store and Apple App Store.

This detailed approach covers the core aspects of developing an app to connect fishing charter boats with potential renters. You can adjust and extend these features based on specific requirements and user feedback.

Certainly! Let's adapt the backend development to use PHP for the API and MySQL for the database. We'll set up a RESTful API using PHP and connect it to a MySQL database.

1. Backend Development with PHP and MySQL

**a. Project Structure

fishing-charter-backend/
│
├── config/
│   ├── database.php
│
├── models/
│   ├── Boat.php
│   └── User.php
│
├── routes/
│   ├── boats.php
│   └── users.php
│
├── index.php
└── .env

**b. Database Configuration

config/database.php:

<?php
$host = 'localhost';
$dbname = 'fishing_charter';
$username = 'root';  // Use your MySQL username
$password = '';      // Use your MySQL password

try {
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}
?>

**c. Model Files

models/Boat.php:

<?php
include_once '../config/database.php';

class Boat {
    private $pdo;

    public function __construct($pdo) {
        $this->pdo = $pdo;
    }

    public function getAllBoats() {
        $stmt = $this->pdo->prepare("SELECT * FROM boats");
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function addBoat($name, $location, $price, $availability, $description, $owner) {
        $stmt = $this->pdo->prepare("INSERT INTO boats (name, location, price, availability, description, owner) VALUES (?, ?, ?, ?, ?, ?)");
        return $stmt->execute([$name, $location, $price, $availability, $description, $owner]);
    }
}
?>

models/User.php:

<?php
include_once '../config/database.php';

class User {
    private $pdo;

    public function __construct($pdo) {
        $this->pdo = $pdo;
    }

    public function register($name, $email, $password, $role) {
        $stmt = $this->pdo->prepare("INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, ?)");
        return $stmt->execute([$name, $email, password_hash($password, PASSWORD_DEFAULT), $role]);
    }

    public function login($email, $password) {
        $stmt = $this->pdo->prepare("SELECT * FROM users WHERE email = ?");
        $stmt->execute([$email]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($user && password_verify($password, $user['password'])) {
            return $user;
        }
        return false;
    }
}
?>

**d. Routes

routes/boats.php:

<?php
include_once '../models/Boat.php';

$boat = new Boat($pdo);

header('Content-Type: application/json');

switch ($_SERVER['REQUEST_METHOD']) {
    case 'GET':
        echo json_encode($boat->getAllBoats());
        break;

    case 'POST':
        $data = json_decode(file_get_contents('php://input'), true);
        if (isset($data['name'], $data['location'], $data['price'], $data['availability'], $data['description'], $data['owner'])) {
            $result = $boat->addBoat($data['name'], $data['location'], $data['price'], $data['availability'], $data['description'], $data['owner']);
            echo json_encode(['success' => $result]);
        } else {
            echo json_encode(['error' => 'Invalid input']);
        }
        break;
}
?>

routes/users.php:

<?php
include_once '../models/User.php';

$user = new User($pdo);

header('Content-Type: application/json');

switch ($_SERVER['REQUEST_METHOD']) {
    case 'POST':
        $data = json_decode(file_get_contents('php://input'), true);
        if (isset($data['name'], $data['email'], $data['password'], $data['role'])) {
            $result = $user->register($data['name'], $data['email'], $data['password'], $data['role']);
            echo json_encode(['success' => $result]);
        } elseif (isset($data['email'], $data['password'])) {
            $result = $user->login($data['email'], $data['password']);
            if ($result) {
                echo json_encode(['success' => true, 'user' => $result]);
            } else {
                echo json_encode(['success' => false, 'message' => 'Invalid credentials']);
            }
        } else {
            echo json_encode(['error' => 'Invalid input']);
        }
        break;
}
?>

**e. Main Entry Point

index.php:

<?php
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');

$request = $_SERVER['REQUEST_URI'];
if (preg_match('/^\/api\/boats/', $request)) {
    include_once 'routes/boats.php';
} elseif (preg_match('/^\/api\/users/', $request)) {
    include_once 'routes/users.php';
} else {
    http_response_code(404);
    echo json_encode(['error' => 'Endpoint not found']);
}
?>

2. Frontend Development

The frontend will be similar to the React Native setup described earlier, but you’ll need to adjust API endpoints to point to your PHP backend. Make sure the frontend makes requests to the correct URL where your PHP server is hosted.

3. Database Schema

Create tables in MySQL for storing boats and users.

SQL to Create Tables:

CREATE TABLE boats (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    location VARCHAR(255) NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    availability BOOLEAN NOT NULL,
    description TEXT NOT NULL,
    owner INT,
    FOREIGN KEY (owner) REFERENCES users(id)
);

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    role ENUM('owner', 'renter') NOT NULL
);

4. Additional Considerations

a. Authentication & Authorization

  • Implement JWT (JSON Web Tokens) or session-based authentication.
  • Secure API endpoints using middleware to check for authenticated requests.

b. Validation and Error Handling

  • Validate incoming data in PHP to ensure it meets required formats.
  • Provide meaningful error messages and handle errors gracefully.

c. Payment Integration

  • Integrate a payment gateway (like Stripe or PayPal) for processing bookings.
  • Ensure secure handling of payment data.

d. Deployment

  • Deploy PHP backend to a web server like Apache or Nginx.
  • Ensure the database is securely hosted and accessible.

e. Scalability

  • Optimize your PHP code and database queries.
  • Consider using caching mechanisms for frequently accessed data.

This approach should give you a robust backend with PHP and MySQL, ready to support your fishing charter app. If you need further customization or advanced features, you can build upon this foundation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment