- Motivation
- Build Status
- Code Style
- Deployment
- Tech/Framework Used
- Features
- Code Examples
- Installation
- API References
- Tests
- How to Use
- Contribute
- Credits
- License
The "Are We There Yet?" project aims to create a virtual trip planner that simplifies the process of planning and booking trips. By leveraging modern technologies such as MongoDB, JWT, Cloudinary, and Stripe, this project provides a seamless and secure experience for users to manage their travel plans. The motivation behind this project is to offer a comprehensive solution that addresses common pain points in trip planning, such as booking management, payment processing, and document handling, all while ensuring user data security and ease of use.
This project is deployed on Render. You can access it at Are We There Yet.
Backend Deployment: Backend Frontend Deployment: Frontend
-
MongoDB Connection Error:
- If you encounter a MongoDB connection error, ensure that your MongoDB connection URI is correctly configured in the
.env
file. - MongoDB does not work on Orange network, so you may need to use a different network.
- If you encounter a MongoDB connection error, ensure that your MongoDB connection URI is correctly configured in the
-
Cloudinary Configuration Error:
- If you face issues with Cloudinary configuration, verify that your Cloudinary URL is correctly set in the
.env
file. - Ensure that your Cloudinary account is active and has the necessary permissions to upload and manage files.
- If you face issues with Cloudinary configuration, verify that your Cloudinary URL is correctly set in the
-
Testing Issues
- Incomplete Test Coverage: If you encounter issues with test coverage, ensure that all critical components and functions are tested.
- CI/CD requires constant updates to the test suite to ensure that new features and changes are adequately tested.
-
Missing Data On Flight or Hotel Pages
- API limits may cause missing data on flight or hotel pages. Ensure that you have the necessary API keys and permissions to access the required data. Upgrading the API plan may help resolve this issue.
-
Deployment Issues
-
If you face deployment issues, check the deployment logs for error messages and warnings. Common deployment issues include missing dependencies, incorrect environment variables, and network connectivity problems.
-
The Free Tier of Render has limitations on the number of services and resources available.
-
This project uses Prettier for code formatting. Below is the Prettier configuration used:
{
"tabWidth": 2,
"printWidth": 120,
"trailingComma": "es5",
"arrowParens": "always",
"semi": true,
"singleQuote": true
}
├── assets
│ ├── badges
│ ├── logo
│ ├── screenshots
|
├── backend
│ ├── src
│ | ├── config
│ | ├── constants
│ | ├── controllers
│ | ├── database
│ | | ├── models
│ | | ├── repositories
│ | ├── exceptions
│ | ├── middlewares
│ │ | ├── auth
│ | ├── routes
│ | ├── services
│ | ├── types
│ | ├── app.ts
│ | ├── server.ts
│ ├── tests
| ├── .env
| ├── .gitignore
| ├── package.json
| ├── tsconfig.json
| ├── tsconfig.test.json
|
├── frontend
│ ├── public
│ ├── src
│ | ├── components
│ | ├── lib
│ | ├── modules
│ │ | ├── Activity
│ │ | ├── etc.
├── .gitignore
├── .prettierrc
├── README.md
# Server Port
PORT=8000
# MongoDB Connection URI
MONGO_URI=mongodb+srv:<username>:<password>@<cluster-url>/<database>
# JWT Token Secret
TOKEN_SECRET=your_token_secret
# Cloudinary Configuration
CLOUDINARY_URL=cloudinary://<api
STRIPE_SECRET_KEY=your_stripe_secret_key
# Email Credentials
EMAIL=your_email
PASSWORD=your_email_password
backend/root -> deployed to production
backend/dev -> where all the development happens. Our code is merged here before being deployed to production
backend/feat/feature -> where you work on your feature
frontend/root -> deployed to production
frontend/dev -> where all the development happens. Our code is merged here before being deployed to production
frontend/feat/feature -> where you work on your feature
![]() |
![]() |
![]() |
---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
- Browse and view all upcoming activities, itineraries, and historical places/museums
- Filter activities by budget, date, category, and ratings
- Sort activities and itineraries by price or ratings
- Filter itineraries by budget, date, preferences, and language
- Filter historical places/museums by tags
- Register as a tourist (requires email, username, password, mobile number, nationality, DOB, job/student status)
- Register as a tour guide, advertiser, or seller (requires username, email, password, and document verification)
- Access step-by-step vacation planning guide
- Choose activity categories
- Search for specific museums, historical places, activities, or itineraries by name, category, or tag
- View available products with details, prices, and reviews
All Guest features plus:
- Secure login with username and password
- Password management (change password, password recovery via email OTP)
- Profile management (view/update personal information)
- Wallet system integration with real-time balance updates
- Book tickets for events, activities, or itineraries
- Pay online using credit/debit cards (Stripe) or wallet
- Receive payment receipts via email
- Cancel bookings up to 48 hours before start time
- View upcoming and past activities/itineraries
- Choose preferred currency for price display
- Bookmark events for later viewing
- Request notifications for booking availability
- Receive event reminders via app and email
- Rate and review tour guides after completed tours
- Rate and review itineraries after completion
- Rate and review attended events/activities
- Rate and review purchased products
- Earn loyalty points on payments
- Progress through three loyalty levels:
- Level 1: Up to 100K points (0.5Ă— points per payment)
- Level 2: Up to 500K points (1Ă— points per payment)
- Level 3: Over 500K points (1.5Ă— points per payment)
- Receive level-based badges
- Redeem points for wallet cash (10000 points = 100 EGP)
- Receive birthday promo codes
- View and search products by name
- Filter products by price
- Sort products by ratings
- Manage wishlist (add, remove, view items)
- Shopping cart functionality:
- Add/remove items
- Adjust quantities
- Add items from wishlist
- Multiple delivery addresses management
- Flexible payment options (wallet, credit card, cash on delivery)
- View order history and status
- Cancel orders with wallet refund
- Use promo codes
- File complaints with title, description, and date
- Track complaint status (pending/resolved)
- View personal complaint history
- Complete profile management (similar to LinkedIn individual profiles)
- Profile picture upload
- Create, view, update, and delete itineraries with detailed information:
- Activities and locations
- Timeline and duration
- Language options
- Pricing
- Available dates and times
- Accessibility details
- Pick-up/drop-off locations
- Activate/deactivate itineraries
- View all created itineraries
- Access sales reports and revenue tracking
- Filter sales reports by activity/itinerary/date/month
- View tourist attendance reports
- Receive notifications for flagged content
- Account deletion request option
- Company profile management (similar to LinkedIn company profiles)
- Logo upload
- Create, view, update, and delete activities with:
- Date and time
- Location (Google Maps integration)
- Pricing and special discounts
- Categories and tags
- Booking availability
- View all created activities
- Access sales reports and revenue tracking
- Filter sales reports by activity/date/month
- View tourist attendance reports
- Receive notifications for flagged content
- Account deletion request option
- Basic profile management (name and description)
- Logo upload
- Product management:
- Add products with details, prices, and quantities
- Upload product images
- Edit product details and pricing
- Archive/unarchive products
- View available quantities and sales
- Receive out-of-stock notifications
- View sales reports
- Filter sales reports by product/date/month
- Account deletion request option
- Manage museums and historical places:
- Add descriptions and pictures
- Set locations and opening hours
- Configure ticket prices (foreign, native, student rates)
- Create and manage historical location tags
- View all created locations and places
- User management:
- Add new administrators
- Add Tourism Governors
- Delete user accounts
- View total user counts and new user statistics
- Review and verify registration documents
- Accept/reject tour guides, advertisers, and sellers
- Flag inappropriate events/itineraries
- Create/manage activity categories
- Create/manage preference tags
- Create promo codes
- View and sort complaints by date
- Filter complaints by status
- View complaint details
- Update complaint status
- Respond to complaints
- Manage product listings
- View product quantities and sales
- Archive/unarchive products
- View comprehensive sales reports
- Monitor revenue from events, itineraries, and gift shop
- Filter sales reports by product/date/month
- Track 10% platform commission on online bookings
Below is an example of an authentication middleware using Express and JWT. This middleware verifies the JWT token and attaches the user payload to the request object. It also includes a function to allow certain paths to be accessed without authentication.
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { logger } from './logger.middleware';
import { ResponseStatusCodes } from '../types/ResponseStatusCodes.types';
interface UserPayload {
userId: string;
accountType: string;
}
declare global {
namespace Express {
interface Request {
user: UserPayload;
}
}
}
const authenticateToken = (req: Request, res: Response, next: NextFunction) => {
const token = req.header('Authorization')?.split(' ')[1];
if (!token) {
logger.error('Access Denied');
res.status(ResponseStatusCodes.UNAUTHORIZED).send('Access Denied');
return;
}
if (req.header('Authorization')?.split(' ')[0] !== 'Bearer') {
logger.error('Invalid Token');
res.status(ResponseStatusCodes.UNAUTHORIZED).send('Invalid Token');
return;
}
try {
const decoded = jwt.verify(token, process.env.TOKEN_SECRET as string) as UserPayload;
req.user = decoded;
next();
} catch (err) {
logger.error('Expired Token:', err instanceof Error ? err.message : 'Unknown error');
res.status(ResponseStatusCodes.FORBIDDEN).send('Expired Token');
}
};
const openPaths = [
{ path: '/api/auth/register', methods: ['POST'] },
{ path: '/api/auth/login', methods: ['POST'] },
{ path: '/api/itineraries/get', methods: ['GET'] },
{ path: '/api/museums/getall', methods: ['GET'] },
{ path: '/api/activities', methods: ['GET'] },
{ path: '/api/attachments', methods: ['POST'] },
{ path: '/api/termsAndConditions', methods: ['GET'] },
{ path: '/api/tags', methods: ['GET'] },
{ path: '/api/categories', methods: ['GET'] },
{ path: '/api/users/forgotPassword', methods: ['POST'] },
{ path: '/api/users/verifyOTP', methods: ['POST'] },
];
const authenticateUnlessOpen = (req: Request, res: Response, next: NextFunction) => {
const isOpenPath = openPaths.some((route) => route.path === req.path && route.methods.includes(req.method));
if (isOpenPath) {
return next();
}
return authenticateToken(req, res, next);
};
export { authenticateToken, authenticateUnlessOpen };
import { Request, Response } from 'express';
import activityRepo from '../database/repositories/activity.repo';
const createActivity = async (req: Request, res: Response) => {
try {
const activity = req.body;
activity.created_by = req.user.userId;
const newActivity = await activityRepo.createActivity(activity);
res.status(201).json({ message: 'Activity created successfully', data: { activityId: newActivity._id } });
} catch (error) {
res.status(400).json({ message: error.message, data: [] });
}
};
export { createActivity };
Below is an example of a React component that uses the Radix UI library to create a custom select dropdown with a trigger component.
import * as React from 'react';
import * as SelectPrimitive from '@radix-ui/react-select';
import { Check, ChevronDown, ChevronUp } from 'lucide-react';
const Select = SelectPrimitive.Root;
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={`flex h-full w-full items-center justify-between rounded-md bg-background p-3 placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-accent-gold disabled:cursor-not-allowed disabled:opacity-50 ${className}`}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
export { Select, SelectTrigger };
import axios from 'axios';
import { NavigateFunction } from 'react-router';
const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_BACK_BASE_URL,
headers: {
'Content-Type': 'application/json',
},
});
axiosInstance.interceptors.request.use(
(config) => {
config.headers = config.headers || {};
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
const currency = localStorage.getItem('currency') || 'EGP';
config.headers['Currency'] = currency;
return config;
},
(error) => {
return Promise.reject(error);
}
);
export const setupInterceptors = (navigate: NavigateFunction) => {
// Response Interceptor
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
if (error.response && [401, 403].includes(error.response.status)) {
localStorage.removeItem('token');
navigate('/register');
}
return Promise.reject(error);
}
);
};
export default axiosInstance;
-
Clone the repository:
git clone https://github.com/yourusername/your-repo-name.git cd your-repo-name
-
Install dependencies:
cd backend npm install cd ../frontend npm install
-
Set up your
.env
file in the backend folder create a.env
file in the backend folder and add the following:# Server Port PORT=8000 # MongoDB Connection URI MONGO_URI=mongodb+srv:<username>:<password>@<cluster-url>/<database> # JWT Token Secret TOKEN_SECRET=your_token_secret # Cloudinary Configuration CLOUDINARY_URL=cloudinary://<api STRIPE_SECRET_KEY=your_stripe_secret_key # Email Credentials EMAIL=your_email PASSWORD=your_email_password
-
Run the app:
cd backend npm start cd ../frontend npm start
This project uses Jest for unit testing. You can run the tests using the following command:
cd backend
npm run test
-
Sign Up / Log In:
- Visit the website and create a new account or log in with your existing credentials.
-
Browse Trips:
- Explore the available trips and activities. Use the search and filter options to find trips that match your preferences.
-
Book a Trip:
- Select a trip and proceed to book it. Fill in the required details and make the payment using the integrated Stripe payment gateway.
-
Manage Bookings:
- View and manage your bookings from your user dashboard. You can cancel or reschedule your bookings as per the cancellation policy.
-
Admin Dashboard:
- If you are an admin, access the admin dashboard to manage users, bookings, and other administrative tasks.
-
Receive Notifications:
- Check your email for booking confirmations and updates. Ensure that you have provided a valid email address.
-
Upload Documents:
- Use the Cloudinary integration to upload and manage documents and images related to your trips.
-
Unit Testing:
- Run unit tests to ensure the reliability and quality of the codebase. Use the following command to run tests:
npm run test
- Run unit tests to ensure the reliability and quality of the codebase. Use the following command to run tests:
By following these steps, you can effectively use the "Are We There Yet?" application to plan and manage your trips.
We welcome contributions to enhance the "Are We There Yet?" project. Here are some ways you can contribute:
-
Report Bugs:
- If you encounter any bugs or issues, please report them by creating an issue on the GitHub repository.
-
Suggest Enhancements:
- Have ideas to improve the project? Suggest new features or enhancements by opening an issue. Some potential enhancements include:
- Real-time currency conversion to provide accurate pricing for users from different countries.
- Integration with additional payment gateways to offer more payment options.
- Improved search and filter functionality to help users find trips more easily.
- Enhanced user profile management with more customization options.
- Adding multi-language support to cater to users from different regions.
- Have ideas to improve the project? Suggest new features or enhancements by opening an issue. Some potential enhancements include:
-
Submit Pull Requests:
- If you have implemented a bug fix or a new feature, submit a pull request. Please ensure your code follows the project's coding standards and includes appropriate tests.
-
Improve Documentation:
- Help us improve the documentation by adding more detailed instructions, code examples, or clarifying existing content.
-
Write Tests:
- Contribute by writing unit tests to increase the test coverage and ensure the reliability of the codebase.
-
Fork the Repository:
- Fork the repository to your GitHub account.
-
Clone the Repository:
- Clone the forked repository to your local machine:
git clone https://github.com/Advanced-computer-lab-2024/Are-we-there-yet cd Are-we-there-yet
- Clone the forked repository to your local machine:
-
Create a Branch:
- Create a new branch for your feature or bug fix:
git checkout -b feature/your-feature-name
- Create a new branch for your feature or bug fix:
-
Make Changes:
- Make your changes to the codebase.
-
Commit Changes:
- Commit your changes with a descriptive commit message:
git commit -m "feat: <your-feature-name>"
- Please ensure your commit messages follow the conventional commit format. For example:
- feat: Add new feature
- fix: Correct typo in file
- refactor: Refactor code for better performance
- test: Add unit tests for component
- Commit your changes with a descriptive commit message:
-
Push Changes:
- Push your changes to your forked repository:
git push origin feature/your-feature-name
- Push your changes to your forked repository:
-
Submit a Pull Request:
- Go to the original repository on GitHub and submit a pull request.
We appreciate your contributions and look forward to collaborating with you to improve the "Are We There Yet?" project.
- Nada Abdel-Fattah: Product Manager (Supervising TA)
- Abdelrahman Mohammed
- Ahmed Gado
- Marwan Mohamed Elsisi
- Mohamed Ahmed El Sawy
- Mohammed Gamal
- Mostafa Hisham Hamdy
- Omar Goba
- Rasheed Atia
- Seifeldin Khaled
- Yousef Yasser
Description | Link |
---|---|
Google Maps | How to load Maps JavaScript API in React |
JWT Explanation | What is JWT and Why Should You Use JWT |
TypeScript | TypeScript Crash Course |
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.