Skip to content

Commit

Permalink
fix: navbar positioning and remove gradient backgrounds
Browse files Browse the repository at this point in the history
Changes made:
- Removed gradient background animations from globals.css
- Fixed navbar positioning to be properly fixed at top
- Removed unnecessary padding and margins from layout
- Simplified glass-morphism styling with minimal transparency
- Updated border styling for better visual consistency
- Cleaned up container and grid padding
- Optimized main content layout structure
  • Loading branch information
AdibSadman192 committed Nov 25, 2024
1 parent 55b5d36 commit 3cab9f9
Show file tree
Hide file tree
Showing 43 changed files with 3,872 additions and 1,622 deletions.
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,68 @@ The platform serves as both a social network and rental service, making it easie
- 🔍 Input Validation
- 📝 Activity Logging

## 🗺️ Project Roadmap

### Phase 1: Foundation (Current)
- [x] Project setup and architecture
- [x] Basic UI components with glass-morphism design
- [x] Authentication system
- [ ] Property listing core features
- [ ] Search and filter functionality

### Phase 2: Enhanced Features (Q1 2025)
- [ ] Advanced property search with map integration
- [ ] Real-time chat between users and property owners
- [ ] Virtual tour integration
- [ ] Review and rating system
- [ ] Payment integration

### Phase 3: Advanced Features (Q2 2025)
- [ ] AI-powered property recommendations
- [ ] Automated rental agreement generation
- [ ] Mobile app development
- [ ] Analytics dashboard for property owners
- [ ] Multi-language support

### Phase 4: Scaling & Optimization (Q3 2025)
- [ ] Performance optimization
- [ ] SEO enhancement
- [ ] Advanced analytics
- [ ] Market analysis tools
- [ ] API marketplace for third-party integrations

## 📊 Current Project Status

| Feature | Status | Notes |
|---------|--------|-------|
| Authentication | ✅ Working | Email and social login implemented |
| User Profiles | ✅ Working | Basic profile management available |
| Property Listing | ⚠️ Partial | Basic listing without advanced features |
| Search System | ⚠️ Partial | Basic search implemented, advanced filters pending |
| Glass-morphism UI | ✅ Working | Complete component library with modern design |
| Responsive Design | ✅ Working | Fully responsive across all devices |
| Chat System | ❌ Pending | Planned for Phase 2 |
| Payment System | ❌ Pending | Planned for Phase 2 |
| Admin Dashboard | ⚠️ Partial | Basic management features available |
| Email Notifications | ✅ Working | Transactional emails implemented |
| Map Integration | ❌ Pending | Planned for Phase 2 |
| Image Upload | ✅ Working | With optimization and CDN delivery |
| Property Analytics | ❌ Pending | Planned for Phase 3 |

## 🎯 Current Sprint Focus
- Enhancing property search functionality
- Implementing advanced filters
- Optimizing image loading and caching
- Improving user experience with smoother transitions
- Adding more interactive elements to property listings

## 🔄 Recent Updates
- Implemented glass-morphism design system
- Added responsive navigation
- Enhanced user authentication flow
- Optimized property listing performance
- Added basic search functionality

## 🛠️ Development

### Tech Stack
Expand Down
91 changes: 91 additions & 0 deletions backend/middleware/authMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const jwt = require('jsonwebtoken');
const asyncHandler = require('express-async-handler');
const User = require('../models/User');

// Protect routes
const protect = asyncHandler(async (req, res, next) => {
let token;

if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
try {
// Get token from header
token = req.headers.authorization.split(' ')[1];

// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);

// Get user from token
req.user = await User.findById(decoded.id).select('-password');

next();
} catch (error) {
console.error(error);
res.status(401);
throw new Error('Not authorized');
}
}

if (!token) {
res.status(401);
throw new Error('Not authorized, no token');
}
});

// Admin middleware
const admin = (req, res, next) => {
if (req.user && req.user.role === 'admin') {
next();
} else {
res.status(401);
throw new Error('Not authorized as admin');
}
};

// Renter middleware
const renter = (req, res, next) => {
if (req.user && req.user.role === 'renter') {
next();
} else {
res.status(401);
throw new Error('Not authorized as renter');
}
};

// Super admin middleware
const superAdmin = (req, res, next) => {
if (req.user && req.user.role === 'superadmin') {
next();
} else {
res.status(401);
throw new Error('Not authorized as super admin');
}
};

// Check if user is authenticated and has required role
const authorize = (roles = []) => {
if (typeof roles === 'string') {
roles = [roles];
}

return [
protect,
(req, res, next) => {
if (roles.length && !roles.includes(req.user.role)) {
res.status(401);
throw new Error(`Not authorized as ${roles.join(', ')}`);
}
next();
}
];
};

module.exports = {
protect,
admin,
renter,
superAdmin,
authorize
};
46 changes: 46 additions & 0 deletions backend/middleware/errorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const ErrorResponse = require('../utils/errorResponse');

const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;

// Log to console for dev
console.error(err.stack.red);

// Mongoose bad ObjectId
if (err.name === 'CastError') {
const message = `Resource not found with id of ${err.value}`;
error = new ErrorResponse(message, 404);
}

// Mongoose duplicate key
if (err.code === 11000) {
const message = 'Duplicate field value entered';
error = new ErrorResponse(message, 400);
}

// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(val => val.message);
error = new ErrorResponse(message, 400);
}

// JWT errors
if (err.name === 'JsonWebTokenError') {
const message = 'Invalid token. Please log in again!';
error = new ErrorResponse(message, 401);
}

if (err.name === 'TokenExpiredError') {
const message = 'Your token has expired! Please log in again.';
error = new ErrorResponse(message, 401);
}

res.status(error.statusCode || 500).json({
success: false,
error: error.message || 'Server Error',
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
});
};

module.exports = errorHandler;
51 changes: 50 additions & 1 deletion backend/models/User.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const crypto = require('crypto');

const userSchema = new mongoose.Schema({
name: {
Expand All @@ -26,7 +27,7 @@ const userSchema = new mongoose.Schema({
phone: {
type: String,
required: [true, 'Phone number is required'],
match: [/^[+]?[\d\s-]{10,}$/, 'Please enter a valid phone number']
match: [/^(\+88)?01[3-9]\d{8}$/, 'Please enter a valid Bangladeshi phone number']
},
nid: {
type: String,
Expand All @@ -44,10 +45,24 @@ const userSchema = new mongoose.Schema({
enum: ['user', 'renter', 'owner', 'student', 'admin', 'super_admin'],
default: 'user'
},
avatar: {
type: String,
default: 'default-avatar.jpg'
},
verified: {
type: Boolean,
default: false
},
verificationToken: String,
verificationTokenExpire: Date,
resetPasswordToken: String,
resetPasswordExpire: Date,
createdAt: {
type: Date,
default: Date.now
}
}, {
timestamps: true
});

// Encrypt password using bcrypt
Expand All @@ -65,4 +80,38 @@ userSchema.methods.matchPassword = async function(enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};

// Generate and hash password token
userSchema.methods.getResetPasswordToken = function() {
// Generate token
const resetToken = crypto.randomBytes(20).toString('hex');

// Hash token and set to resetPasswordToken field
this.resetPasswordToken = crypto
.createHash('sha256')
.update(resetToken)
.digest('hex');

// Set expire
this.resetPasswordExpire = Date.now() + 10 * 60 * 1000; // 10 minutes

return resetToken;
};

// Generate email verification token
userSchema.methods.getVerificationToken = function() {
// Generate token
const verificationToken = crypto.randomBytes(20).toString('hex');

// Hash token and set to verificationToken field
this.verificationToken = crypto
.createHash('sha256')
.update(verificationToken)
.digest('hex');

// Set expire
this.verificationTokenExpire = Date.now() + 24 * 60 * 60 * 1000; // 24 hours

return verificationToken;
};

module.exports = mongoose.model('User', userSchema);
10 changes: 10 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
},
"dependencies": {
"bcryptjs": "^2.4.3",
"colors": "^1.4.0",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
Expand Down
12 changes: 12 additions & 0 deletions backend/utils/errorResponse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class ErrorResponse extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;

Error.captureStackTrace(this, this.constructor);
}
}

module.exports = ErrorResponse;
63 changes: 63 additions & 0 deletions frontend/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
jest: true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:@next/next/recommended',
'next/core-web-vitals',
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
},
plugins: ['react', 'react-hooks', '@next/next'],
settings: {
react: {
version: 'detect',
},
},
rules: {
// React specific rules
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'react/display-name': 'off',
'react/no-unescaped-entities': 'off',

// Next.js specific rules
'@next/next/no-img-element': 'warn',
'@next/next/no-html-link-for-pages': 'error',

// React Hooks rules
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',

// General JavaScript/ES6 rules
'no-unused-vars': ['warn', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}],
'no-console': ['warn', { allow: ['warn', 'error'] }],
'prefer-const': 'warn',
'no-var': 'error',

// Import rules
'import/no-anonymous-default-export': 'off',
},
globals: {
React: 'writable',
JSX: 'writable',
Promise: 'writable',
Set: 'writable',
Map: 'writable',
},
};
Loading

0 comments on commit 3cab9f9

Please sign in to comment.