Initial commit - Rentall App

- Full-stack rental marketplace application
- React frontend with TypeScript
- Node.js/Express backend with JWT authentication
- Features: item listings, rental requests, calendar availability, user profiles
This commit is contained in:
jackiettran
2025-07-15 21:21:09 -04:00
commit c09384e3ea
53 changed files with 24425 additions and 0 deletions

110
backend/models/Item.js Normal file
View File

@@ -0,0 +1,110 @@
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');
const Item = sequelize.define('Item', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
description: {
type: DataTypes.TEXT,
allowNull: false
},
tags: {
type: DataTypes.ARRAY(DataTypes.STRING),
defaultValue: []
},
pickUpAvailable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
localDeliveryAvailable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
localDeliveryRadius: {
type: DataTypes.INTEGER,
validate: {
min: 1,
max: 100
}
},
shippingAvailable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
inPlaceUseAvailable: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
pricePerHour: {
type: DataTypes.DECIMAL(10, 2)
},
pricePerDay: {
type: DataTypes.DECIMAL(10, 2)
},
replacementCost: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false
},
location: {
type: DataTypes.STRING,
allowNull: false
},
latitude: {
type: DataTypes.DECIMAL(10, 8)
},
longitude: {
type: DataTypes.DECIMAL(11, 8)
},
images: {
type: DataTypes.ARRAY(DataTypes.STRING),
defaultValue: []
},
availability: {
type: DataTypes.BOOLEAN,
defaultValue: true
},
specifications: {
type: DataTypes.JSONB,
defaultValue: {}
},
rules: {
type: DataTypes.TEXT
},
minimumRentalDays: {
type: DataTypes.INTEGER,
defaultValue: 1
},
maximumRentalDays: {
type: DataTypes.INTEGER
},
needsTraining: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
unavailablePeriods: {
type: DataTypes.JSONB,
defaultValue: []
},
ownerId: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
}
});
module.exports = Item;

76
backend/models/Rental.js Normal file
View File

@@ -0,0 +1,76 @@
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');
const Rental = sequelize.define('Rental', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
itemId: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'Items',
key: 'id'
}
},
renterId: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
ownerId: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
startDate: {
type: DataTypes.DATE,
allowNull: false
},
endDate: {
type: DataTypes.DATE,
allowNull: false
},
totalAmount: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false
},
status: {
type: DataTypes.ENUM('pending', 'confirmed', 'active', 'completed', 'cancelled'),
defaultValue: 'pending'
},
paymentStatus: {
type: DataTypes.ENUM('pending', 'paid', 'refunded'),
defaultValue: 'pending'
},
deliveryMethod: {
type: DataTypes.ENUM('pickup', 'delivery'),
defaultValue: 'pickup'
},
deliveryAddress: {
type: DataTypes.TEXT
},
notes: {
type: DataTypes.TEXT
},
rating: {
type: DataTypes.INTEGER,
validate: {
min: 1,
max: 5
}
},
review: {
type: DataTypes.TEXT
}
});
module.exports = Rental;

66
backend/models/User.js Normal file
View File

@@ -0,0 +1,66 @@
const { DataTypes } = require('sequelize');
const sequelize = require('../config/database');
const bcrypt = require('bcryptjs');
const User = sequelize.define('User', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
username: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
},
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING,
allowNull: false
},
phone: {
type: DataTypes.STRING
},
address: {
type: DataTypes.TEXT
},
profileImage: {
type: DataTypes.STRING
},
isVerified: {
type: DataTypes.BOOLEAN,
defaultValue: false
}
}, {
hooks: {
beforeCreate: async (user) => {
user.password = await bcrypt.hash(user.password, 10);
},
beforeUpdate: async (user) => {
if (user.changed('password')) {
user.password = await bcrypt.hash(user.password, 10);
}
}
}
});
User.prototype.comparePassword = async function(password) {
return bcrypt.compare(password, this.password);
};
module.exports = User;

22
backend/models/index.js Normal file
View File

@@ -0,0 +1,22 @@
const sequelize = require('../config/database');
const User = require('./User');
const Item = require('./Item');
const Rental = require('./Rental');
User.hasMany(Item, { as: 'ownedItems', foreignKey: 'ownerId' });
Item.belongsTo(User, { as: 'owner', foreignKey: 'ownerId' });
User.hasMany(Rental, { as: 'rentalsAsRenter', foreignKey: 'renterId' });
User.hasMany(Rental, { as: 'rentalsAsOwner', foreignKey: 'ownerId' });
Item.hasMany(Rental, { as: 'rentals', foreignKey: 'itemId' });
Rental.belongsTo(Item, { as: 'item', foreignKey: 'itemId' });
Rental.belongsTo(User, { as: 'renter', foreignKey: 'renterId' });
Rental.belongsTo(User, { as: 'owner', foreignKey: 'ownerId' });
module.exports = {
sequelize,
User,
Item,
Rental
};