├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── admin
├── .env.example
├── .gitignore
├── Dockerfile
├── README.md
├── docker-compose.yml
├── eslint.config.js
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│ └── vite.svg
├── render.yaml
├── src
│ ├── App.jsx
│ ├── assets
│ │ └── administrator.png
│ ├── components
│ │ ├── ErrorFallback.jsx
│ │ ├── Navbar.jsx
│ │ ├── ProtectedRoute.jsx
│ │ └── login.jsx
│ ├── index.css
│ ├── main.jsx
│ └── pages
│ │ ├── Add.jsx
│ │ ├── Appointments.jsx
│ │ ├── Dashboard.jsx
│ │ ├── List.jsx
│ │ └── Update.jsx
├── tailwind.config.js
├── vercel.json
└── vite.config.js
├── backend
├── .env.example
├── .gitignore
├── Dockerfile
├── config
│ ├── config.js
│ ├── imagekit.js
│ ├── mongodb.js
│ └── nodemailer.js
├── controller
│ ├── Usercontroller.js
│ ├── adminController.js
│ ├── appointmentController.js
│ ├── formcontroller.js
│ ├── newscontroller.js
│ ├── productcontroller.js
│ └── propertyController.js
├── docker-compose.yml
├── email.js
├── middleware
│ ├── authmiddleware.js
│ ├── multer.js
│ └── statsMiddleware.js
├── models
│ ├── Usermodel.js
│ ├── appointmentModel.js
│ ├── formmodel.js
│ ├── newsmodel.js
│ ├── propertymodel.js
│ └── statsModel.js
├── package-lock.json
├── package.json
├── render.yaml
├── routes
│ ├── ProductRouter.js
│ ├── UserRoute.js
│ ├── adminRoute.js
│ ├── appointmentRoute.js
│ ├── formrouter.js
│ ├── newsRoute.js
│ └── propertyRoutes.js
├── server.js
├── services
│ ├── aiService.js
│ └── firecrawlService.js
└── vercel.json
├── frontend
├── .env.example
├── .gitignore
├── README.md
├── eslint.config.js
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│ ├── robots.txt
│ └── sitemap.xml
├── src
│ ├── App.jsx
│ ├── assets
│ │ ├── Amazon_logo.svg
│ │ ├── Booking.com_logo.svg
│ │ ├── Google_2015_logo.svg
│ │ ├── airbnb.svg
│ │ ├── blogdata.js
│ │ ├── data
│ │ │ └── teammemberdata.js
│ │ ├── featuredata.js
│ │ ├── home-regular-24.png
│ │ ├── images
│ │ │ ├── aayush.png
│ │ │ ├── about_enhanced.jpg
│ │ │ ├── heroimage.png
│ │ │ └── nakul.png
│ │ ├── logo.js
│ │ ├── microsoft.svg
│ │ ├── properties.js
│ │ ├── stepsdata.js
│ │ ├── testimonialdata.js
│ │ └── user_logo.png
│ ├── components
│ │ ├── Blog.jsx
│ │ ├── Companies.jsx
│ │ ├── Features.jsx
│ │ ├── Hero.jsx
│ │ ├── Navbar.jsx
│ │ ├── Notfound.jsx
│ │ ├── SEO
│ │ │ ├── AiHubSEO.jsx
│ │ │ └── StructuredData.jsx
│ │ ├── Steps.jsx
│ │ ├── aboutus
│ │ │ ├── Benefit.jsx
│ │ │ ├── Contup.jsx
│ │ │ ├── Hero.jsx
│ │ │ ├── Milestone.jsx
│ │ │ ├── Mission.jsx
│ │ │ ├── Team.jsx
│ │ │ ├── Teammember.jsx
│ │ │ └── Values.jsx
│ │ ├── ai
│ │ │ ├── AnalysisDisplay.jsx
│ │ │ ├── Header.jsx
│ │ │ ├── LocationTrends.jsx
│ │ │ ├── PropertyCard.jsx
│ │ │ └── SearchForm.jsx
│ │ ├── contact
│ │ │ ├── ContactHero.jsx
│ │ │ ├── ContactInfo.jsx
│ │ │ ├── Formhandle.jsx
│ │ │ ├── InfoItem.jsx
│ │ │ ├── contactForm.jsx
│ │ │ └── useContactform.jsx
│ │ ├── footer.jsx
│ │ ├── forgetpassword.jsx
│ │ ├── login.jsx
│ │ ├── properties
│ │ │ ├── Filtersection.jsx
│ │ │ ├── Propertiespage.jsx
│ │ │ ├── Propertycard.jsx
│ │ │ ├── ScheduleViewing.jsx
│ │ │ ├── Searchbar.jsx
│ │ │ └── propertydetail.jsx
│ │ ├── propertiesshow.jsx
│ │ ├── resetpassword.jsx
│ │ ├── signup.jsx
│ │ └── testimonial.jsx
│ ├── context
│ │ └── AuthContext.jsx
│ ├── index.css
│ ├── main.jsx
│ ├── pages
│ │ ├── About.jsx
│ │ ├── Aiagent.jsx
│ │ ├── Contact.jsx
│ │ ├── Home.jsx
│ │ └── Properties.jsx
│ ├── services
│ │ └── api.js
│ └── styles
│ │ └── auth.js
├── tailwind.config.cjs
├── vercel.json
└── vite.config.js
├── package-lock.json
└── package.json
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These owners will be the default owners for everything in the repo
2 | * @AAYUSH412
3 |
4 | # Backend code owners
5 | /backend/ @AAYUSH412
6 |
7 | # Frontend code owners
8 | /frontend/ @AAYUSH412
9 |
10 | # Admin dashboard code owners
11 | /admin/ @AAYUSH412
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help improve BuildEstate
4 | title: "[BUG] "
5 | labels: bug
6 | assignees: ''
7 | ---
8 |
9 | **Describe the bug**
10 | A clear and concise description of what the bug is.
11 |
12 | **To Reproduce**
13 | Steps to reproduce the behavior:
14 | 1. Go to '...'
15 | 2. Click on '....'
16 | 3. Scroll down to '....'
17 | 4. See error
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **Environment (please complete the following information):**
26 | - Device: [e.g., Desktop, iPhone 12]
27 | - OS: [e.g., Windows 10, iOS 16]
28 | - Browser: [e.g., Chrome 112, Safari 16]
29 | - BuildEstate Version: [e.g., 1.0.0]
30 |
31 | **Additional context**
32 | Add any other context about the problem here.
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for BuildEstate
4 | title: "[FEATURE] "
5 | labels: enhancement
6 | assignees: ''
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Describe alternatives you've considered**
16 | A clear and concise description of any alternative solutions or features you've considered.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
21 | **Impact & Scope**
22 | How would this feature benefit users? Does it affect a small or large part of the application?
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 |
4 | ## Related Issue
5 |
6 |
7 |
8 | ## Motivation and Context
9 |
10 |
11 | ## How Has This Been Tested?
12 |
13 |
14 |
15 | ## Screenshots (if appropriate)
16 |
17 | ## Types of changes
18 |
19 | - [ ] Bug fix (non-breaking change which fixes an issue)
20 | - [ ] New feature (non-breaking change which adds functionality)
21 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
22 |
23 | ## Checklist
24 |
25 | - [ ] My code follows the code style of this project.
26 | - [ ] My change requires a change to the documentation.
27 | - [ ] I have updated the documentation accordingly.
28 | - [ ] I have added tests to cover my changes.
29 | - [ ] All new and existing tests passed.
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Node.js
2 | node_modules/
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 |
7 | # Logs
8 | logs/
9 | *.log
10 | *.log.*
11 |
12 | # Dependency directories
13 | node_modules/
14 | jspm_packages/
15 |
16 | # Optional npm cache directory
17 | .npm
18 |
19 | # Optional eslint cache
20 | .eslintcache
21 |
22 | # Optional REPL history
23 | .node_repl_history
24 |
25 | # dotenv environment variables file
26 | .env
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
32 | # MacOS
33 | .DS_Store
34 |
35 | # Editor directories and files
36 | .idea/
37 | .vscode/
38 | *.suo
39 | *.ntvs*
40 | *.njsproj
41 | *.sln
42 | *.sw?
43 |
44 | # Build directories
45 | build/
46 | dist/
47 |
48 | # Coverage directory used by tools like istanbul
49 | coverage/
50 |
51 | # Miscellaneous
52 | *.pem
53 | *.p12
54 | *.key
55 | *.crt
56 | *.csr
57 | *.pfx
58 |
59 | # React Native
60 | .expo/
61 | .expo-shared/
62 |
63 | # Parcel-bundler cache (https://parceljs.org/)
64 | .cache
65 | .parcel-cache
66 |
67 | # SvelteKit build / generated files
68 | .svelte-kit
69 |
70 | # Storybook files
71 | out/
72 | .storybook-out/
73 |
74 | # Do NOT ignore these important files
75 | !package-lock.json
76 | !yarn.lock
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Project maintainers are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies within all community spaces, and also applies when
49 | an individual is officially representing the community in public spaces.
50 |
51 | ## Enforcement
52 |
53 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
54 | reported to the community leaders responsible for enforcement at
55 | aayushvaghela12@gmail.com.
56 | All complaints will be reviewed and investigated promptly and fairly.
57 |
58 | ## Attribution
59 |
60 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
61 | version 2.0, available at
62 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to BuildEstate
2 |
3 | Thank you for considering contributing to BuildEstate! This document outlines the process for contributing to this project.
4 |
5 | ## Code of Conduct
6 |
7 | By participating in this project, you agree to maintain a respectful and inclusive environment for everyone. Please be courteous and respectful in your interactions.
8 |
9 | ## How to Contribute
10 |
11 | There are several ways you can contribute to the BuildEstate project:
12 |
13 | 1. **Reporting Bugs**: Open an issue describing the bug, how to reproduce it, and any potential solutions.
14 | 2. **Suggesting Features**: Open an issue describing your feature idea and how it would benefit the project.
15 | 3. **Submitting Code**: Make a pull request with your code changes.
16 | 4. **Improving Documentation**: Help improve the documentation through pull requests.
17 |
18 | ## Development Process
19 |
20 | ### Setup Local Environment
21 |
22 | 1. Fork the repository
23 | 2. Clone your fork:
24 | ```bash
25 | git clone https://github.com/AAYUSH412/Real-Estate-Website.git
26 | cd buildEstate
27 | ```
28 | 3. Install dependencies:
29 | ```bash
30 | # Install backend dependencies
31 | cd backend
32 | npm install
33 |
34 | # Install frontend dependencies
35 | cd ../frontend
36 | npm install
37 |
38 | # Install admin dependencies
39 | cd ../admin
40 | npm install
41 | ```
42 | 4. Create your `.env.local` files as described in the [README.md](README.md)
43 |
44 | ### Making Changes
45 |
46 | 1. Create a new branch:
47 | ```bash
48 | git checkout -b feature/your-feature-name
49 | ```
50 | 2. Make your changes
51 | 3. Test your changes thoroughly
52 | 4. Commit your changes:
53 | ```bash
54 | git commit -m "Brief description of changes"
55 | ```
56 | 5. Push to your fork:
57 | ```bash
58 | git push origin feature/your-feature-name
59 | ```
60 | 6. Create a pull request from your branch to the main repository
61 |
62 | ## Pull Request Guidelines
63 |
64 | - Ensure your code follows the existing style patterns and practices
65 | - Include clear descriptions of the changes made
66 | - Update documentation if necessary
67 | - Make sure all tests pass
68 | - Keep pull requests focused on a single concern/feature
69 |
70 | ## Code Style
71 |
72 | - Follow the existing code style and formatting
73 | - Use meaningful variable and function names
74 | - Write comments for complex logic
75 | - Include JSDoc comments for functions
76 |
77 | ## Testing
78 |
79 | - Add appropriate tests for new features
80 | - Ensure all existing tests pass
81 | - Manual testing should be performed on different devices and browsers
82 |
83 | ## Documentation
84 |
85 | - Update the README.md if necessary
86 | - Document API changes in the appropriate documentation files
87 | - Keep code comments up-to-date
88 |
89 | ## Questions?
90 |
91 | If you have any questions or need help, feel free to open an issue or contact the maintainers.
92 |
93 | Thank you for contributing to BuildEstate!
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Aayush Vaghela
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are currently being supported with security updates.
6 |
7 | | Version | Supported |
8 | | ------- | ------------------ |
9 | | 1.0.x | :white_check_mark: |
10 | | < 1.0 | :x: |
11 |
12 | ## Reporting a Vulnerability
13 |
14 | We take the security of BuildEstate seriously. If you believe you've found a security vulnerability, please follow these steps:
15 |
16 | 1. **Do not disclose the vulnerability publicly**
17 | 2. **Email the details to aayushvaghela12@gmail.com**
18 | - Include a description of the vulnerability
19 | - Steps to reproduce the issue
20 | - Potential impact
21 | - Suggestions for remediation if available
22 |
23 | ## What to expect
24 | - We will acknowledge receipt of your report within 48 hours
25 | - We will provide an initial assessment of the report within 5 business days
26 | - We will keep you informed about our progress in addressing the issue
27 | - After the issue is resolved, we may publicly acknowledge your responsible disclosure
28 |
29 | Thank you for helping keep BuildEstate and our users safe!
--------------------------------------------------------------------------------
/admin/.env.example:
--------------------------------------------------------------------------------
1 | VITE_BACKEND_URL=http://localhost:4000
--------------------------------------------------------------------------------
/admin/.gitignore:
--------------------------------------------------------------------------------
1 | # Node.js
2 | node_modules/
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 |
7 | # Logs
8 | logs/
9 | *.log
10 | *.log.*
11 |
12 | # Dependency directories
13 | node_modules/
14 | jspm_packages/
15 |
16 | # Optional npm cache directory
17 | .npm
18 |
19 | # Optional eslint cache
20 | .eslintcache
21 |
22 | # Optional REPL history
23 | .node_repl_history
24 |
25 | # dotenv environment variables file
26 | .env
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
32 | # MacOS
33 | .DS_Store
34 |
35 | # Editor directories and files
36 | .idea/
37 | .vscode/
38 | *.suo
39 | *.ntvs*
40 | *.njsproj
41 | *.sln
42 | *.sw?
43 |
44 | # Build directories
45 | build/
46 | dist/
47 |
48 | # Coverage directory used by tools like istanbul
49 | coverage/
50 |
51 | # Miscellaneous
52 | *.pem
53 | *.p12
54 | *.key
55 | *.crt
56 | *.csr
57 | *.pfx
58 |
59 | # React Native
60 | .expo/
61 | .expo-shared/
62 |
63 | # Parcel-bundler cache (https://parceljs.org/)
64 | .cache
65 | .parcel-cache
66 |
67 | # SvelteKit build / generated files
68 | .svelte-kit
69 |
70 | # Storybook files
71 | out/
72 | .storybook-out/
--------------------------------------------------------------------------------
/admin/Dockerfile:
--------------------------------------------------------------------------------
1 | # Base image
2 | FROM node:18-alpine
3 |
4 | # Set working directory
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm install
12 |
13 | # Copy project files
14 | COPY . .
15 |
16 | # Build the app
17 | RUN npm run build
18 |
19 | # Start command
20 | CMD ["npm", "run", "dev"]
--------------------------------------------------------------------------------
/admin/README.md:
--------------------------------------------------------------------------------
1 | # React + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
--------------------------------------------------------------------------------
/admin/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 |
3 | services:
4 | admin:
5 | build: .
6 | volumes:
7 | - .:/app
8 | - /app/node_modules
--------------------------------------------------------------------------------
/admin/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import react from 'eslint-plugin-react'
4 | import reactHooks from 'eslint-plugin-react-hooks'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 |
7 | export default [
8 | { ignores: ['dist'] },
9 | {
10 | files: ['**/*.{js,jsx}'],
11 | languageOptions: {
12 | ecmaVersion: 2020,
13 | globals: globals.browser,
14 | parserOptions: {
15 | ecmaVersion: 'latest',
16 | ecmaFeatures: { jsx: true },
17 | sourceType: 'module',
18 | },
19 | },
20 | settings: { react: { version: '18.3' } },
21 | plugins: {
22 | react,
23 | 'react-hooks': reactHooks,
24 | 'react-refresh': reactRefresh,
25 | },
26 | rules: {
27 | ...js.configs.recommended.rules,
28 | ...react.configs.recommended.rules,
29 | ...react.configs['jsx-runtime'].rules,
30 | ...reactHooks.configs.recommended.rules,
31 | 'react/jsx-no-target-blank': 'off',
32 | 'react-refresh/only-export-components': [
33 | 'warn',
34 | { allowConstantExport: true },
35 | ],
36 | },
37 | },
38 | ]
39 |
--------------------------------------------------------------------------------
/admin/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Admin Page
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/admin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "admin",
3 | "private": true,
4 | "version": "1.0.0",
5 | "type": "module",
6 | "description": "BuildEstate admin dashboard for property management",
7 | "author": "Aayush Vaghela",
8 | "license": "MIT",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/AAYUSH412/Real-Estate-Website"
12 | },
13 | "scripts": {
14 | "dev": "vite",
15 | "build": "vite build",
16 | "lint": "eslint .",
17 | "preview": "vite preview"
18 | },
19 | "dependencies": {
20 | "axios": "^1.7.9",
21 | "chart.js": "^4.4.7",
22 | "flowbite-react": "^0.10.2",
23 | "framer-motion": "^12.4.2",
24 | "lucide-react": "^0.469.0",
25 | "prop-types": "^15.8.1",
26 | "react": "^18.3.1",
27 | "react-chartjs-2": "^5.3.0",
28 | "react-dom": "^18.3.1",
29 | "react-error-boundary": "^5.0.0",
30 | "react-hot-toast": "^2.5.1",
31 | "react-icon": "^1.0.0",
32 | "react-icons": "^5.4.0",
33 | "react-router-dom": "^7.1.1",
34 | "react-toastify": "^11.0.2"
35 | },
36 | "devDependencies": {
37 | "@eslint/js": "^9.17.0",
38 | "@types/react": "^18.3.18",
39 | "@types/react-dom": "^18.3.5",
40 | "@vitejs/plugin-react": "^4.3.4",
41 | "autoprefixer": "^10.4.20",
42 | "eslint": "^9.17.0",
43 | "eslint-plugin-react": "^7.37.2",
44 | "eslint-plugin-react-hooks": "^5.0.0",
45 | "eslint-plugin-react-refresh": "^0.4.16",
46 | "globals": "^15.14.0",
47 | "postcss": "^8.4.49",
48 | "tailwindcss": "^3.4.17",
49 | "vite": "^6.0.5"
50 | }
51 | }
--------------------------------------------------------------------------------
/admin/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/admin/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/admin/render.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | - type: web
3 | name: real-estate-admin
4 | env: static
5 | buildCommand: npm install && npm run build
6 | staticPublishPath: ./dist
7 | envVars:
8 | - key: VITE_BACKEND_URL
9 | value: https://real-estate-website-backend-fullcode.onrender.com
10 | routes:
11 | - type: rewrite
12 | source: /*
13 | destination: /index.html
--------------------------------------------------------------------------------
/admin/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { Routes, Route, Navigate } from "react-router-dom";
2 | import { Toaster } from "react-hot-toast";
3 | import { ErrorBoundary } from "react-error-boundary";
4 | import { motion, AnimatePresence } from "framer-motion";
5 |
6 | // Components
7 | import Navbar from "./components/Navbar";
8 | import ProtectedRoute from "./components/ProtectedRoute";
9 | import ErrorFallback from "./components/ErrorFallback";
10 |
11 | // Pages
12 | import Login from "./components/login";
13 | import Dashboard from "./pages/Dashboard";
14 | import List from "./pages/List";
15 | import Add from "./pages/Add";
16 | import Update from "./pages/Update";
17 | import Appointments from "./pages/Appointments";
18 |
19 | // Config
20 | export const backendurl = import.meta.env.VITE_BACKEND_URL;
21 |
22 | // Page transition variants
23 | const pageVariants = {
24 | initial: { opacity: 0, y: 20 },
25 | animate: { opacity: 1, y: 0 },
26 | exit: { opacity: 0, y: -20 }
27 | };
28 |
29 | const App = () => {
30 | return (
31 | window.location.reload()}
34 | >
35 |
36 |
37 |
38 |
45 |
46 | {/* Public Routes */}
47 | } />
48 | } />
49 |
50 | {/* Protected Routes */}
51 | }>
52 | } />
53 | } />
54 | } />
55 | } />
56 | } />
57 |
58 |
59 | {/* 404 Route */}
60 | } />
61 |
62 |
63 |
64 |
65 | {/* Toast Notifications */}
66 |
76 |
77 |
78 | );
79 | };
80 |
81 | export default App;
--------------------------------------------------------------------------------
/admin/src/assets/administrator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AAYUSH412/Real-Estate-Website/24a68ddd4502b6680132ab32ee6b25eac5844a95/admin/src/assets/administrator.png
--------------------------------------------------------------------------------
/admin/src/components/ErrorFallback.jsx:
--------------------------------------------------------------------------------
1 | import { AlertCircle } from "lucide-react";
2 | import Proptypes from "prop-types";
3 |
4 | const ErrorFallback = ({ error, resetErrorBoundary }) => {
5 | return (
6 |
7 |
8 |
9 |
10 | Something went wrong
11 |
12 |
{error.message}
13 |
18 | Try again
19 |
20 |
21 |
22 | );
23 | };
24 |
25 | ErrorFallback.propTypes = {
26 | error: Proptypes.object.isRequired,
27 | resetErrorBoundary: Proptypes.func.isRequired
28 | };
29 |
30 |
31 | export default ErrorFallback;
--------------------------------------------------------------------------------
/admin/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Link, useNavigate, useLocation } from 'react-router-dom';
3 | import { motion } from 'framer-motion';
4 | import {
5 | Home,
6 | List,
7 | PlusSquare,
8 | Calendar,
9 | Menu,
10 | X,
11 | LogOut,
12 | LayoutDashboard,
13 | Database
14 | } from 'lucide-react';
15 |
16 | const Navbar = () => {
17 | const navigate = useNavigate();
18 | const location = useLocation();
19 | const [isMenuOpen, setIsMenuOpen] = useState(false);
20 |
21 | const isActive = (path) => {
22 | return location.pathname === path;
23 | };
24 |
25 | const handleLogout = () => {
26 | localStorage.removeItem('token');
27 | localStorage.removeItem('isAdmin');
28 | navigate('/login');
29 | };
30 |
31 | const toggleMenu = () => {
32 | setIsMenuOpen(!isMenuOpen);
33 | };
34 |
35 | const navItems = [
36 | { path: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
37 | { path: '/list', label: 'Properties', icon: List },
38 | { path: '/add', label: 'Add Property', icon: PlusSquare },
39 | { path: '/appointments', label: 'Appointments', icon: Calendar },
40 | ];
41 |
42 | return (
43 |
44 |
45 |
46 | {/* Logo */}
47 |
48 |
49 |
50 |
51 |
Admin Panel
52 |
53 |
54 | {/* Desktop Navigation */}
55 |
56 | {navItems.map((item) => (
57 |
66 |
67 |
68 | {item.label}
69 |
70 |
71 | ))}
72 |
73 |
77 |
78 |
79 | Logout
80 |
81 |
82 |
83 |
84 | {/* Mobile Menu Button */}
85 |
86 |
90 | {isMenuOpen ? (
91 |
92 | ) : (
93 |
94 | )}
95 |
96 |
97 |
98 |
99 |
100 | {/* Mobile Menu */}
101 | {isMenuOpen && (
102 |
108 |
109 | {navItems.map((item) => (
110 |
setIsMenuOpen(false)}
119 | >
120 |
121 |
122 | {item.label}
123 |
124 |
125 | ))}
126 |
127 |
131 |
132 |
133 | Logout
134 |
135 |
136 |
137 |
138 | )}
139 |
140 | );
141 | };
142 |
143 | export default Navbar;
--------------------------------------------------------------------------------
/admin/src/components/ProtectedRoute.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Navigate, Outlet } from 'react-router-dom';
3 |
4 | const ProtectedRoute = () => {
5 | const token = localStorage.getItem('token');
6 | const isAdmin = localStorage.getItem('isAdmin');
7 |
8 | // Check both token and admin status
9 | const isAuthenticated = token && isAdmin === 'true';
10 |
11 | // Verify token expiration
12 | const isTokenExpired = () => {
13 | if (!token) return true;
14 | try {
15 | const tokenData = JSON.parse(atob(token.split('.')[1]));
16 | return tokenData.exp * 1000 < Date.now();
17 | } catch (error) {
18 | console.error('Error verifying token:', error);
19 | return true;
20 | }
21 | };
22 |
23 | if (!isAuthenticated || isTokenExpired()) {
24 | // Clean up storage on invalid auth
25 | localStorage.removeItem('token');
26 | localStorage.removeItem('isAdmin');
27 | return ;
28 | }
29 |
30 | // Return Outlet for nested routes
31 | return ;
32 | };
33 |
34 | export default ProtectedRoute;
--------------------------------------------------------------------------------
/admin/src/components/login.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import { FaEye, FaEyeSlash } from "react-icons/fa";
4 | import { toast } from "react-hot-toast";
5 | import axios from "axios";
6 |
7 | const backendUrl = import.meta.env.VITE_BACKEND_URL;
8 |
9 | const Login = () => {
10 | const [email, setEmail] = useState("");
11 | const [password, setPassword] = useState("");
12 | const [showPassword, setShowPassword] = useState(false);
13 | const [loading, setLoading] = useState(false);
14 | const navigate = useNavigate();
15 |
16 | const handleSubmit = async (e) => {
17 | e.preventDefault();
18 | setLoading(true);
19 |
20 | try {
21 | // Change the endpoint to /api/users/admin for admin login
22 | const response = await axios.post(`${backendUrl}/api/users/admin`, {
23 | email,
24 | password
25 | });
26 |
27 | if (response.data.success) {
28 | // Store the admin token
29 | localStorage.setItem('token', response.data.token);
30 | localStorage.setItem('isAdmin', 'true');
31 |
32 | toast.success("Admin login successful!");
33 | navigate("/dashboard");
34 | } else {
35 | toast.error(response.data.message || "Login failed");
36 | }
37 | } catch (error) {
38 | console.error('Error logging in:', error);
39 | toast.error(error.response?.data?.message || 'Invalid admin credentials');
40 | } finally {
41 | setLoading(false);
42 | }
43 | };
44 |
45 | return (
46 |
47 |
48 |
49 |
50 |
51 | Admin Login
52 |
53 |
113 |
114 |
115 |
116 |
117 | );
118 | };
119 |
120 | export default Login;
--------------------------------------------------------------------------------
/admin/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/admin/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import { BrowserRouter } from 'react-router-dom';
4 | import App from './App';
5 | import './index.css';
6 |
7 | ReactDOM.createRoot(document.getElementById('root')).render(
8 |
9 |
10 |
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/admin/tailwind.config.js:
--------------------------------------------------------------------------------
1 | // tailwind.config.js
2 | module.exports = {
3 | content: [
4 | "./index.html",
5 | "./src/**/*.{js,jsx,ts,tsx}",
6 | ],
7 | darkMode: 'class',
8 | theme: {
9 | extend: {
10 | colors: {
11 | primary: {"50":"#eff6ff","100":"#dbeafe","200":"#bfdbfe","300":"#93c5fd","400":"#60a5fa","500":"#3b82f6","600":"#2563eb","700":"#1d4ed8","800":"#1e40af","900":"#1e3a8a","950":"#172554"}
12 | }
13 | },
14 | fontFamily: {
15 | 'body': [
16 | 'Inter',
17 | 'ui-sans-serif',
18 | 'system-ui',
19 | '-apple-system',
20 | 'system-ui',
21 | 'Segoe UI',
22 | 'Roboto',
23 | 'Helvetica Neue',
24 | 'Arial',
25 | 'Noto Sans',
26 | 'sans-serif',
27 | 'Apple Color Emoji',
28 | 'Segoe UI Emoji',
29 | 'Segoe UI Symbol',
30 | 'Noto Color Emoji'
31 | ],
32 | 'sans': [
33 | 'Inter',
34 | 'ui-sans-serif',
35 | 'system-ui',
36 | '-apple-system',
37 | 'system-ui',
38 | 'Segoe UI',
39 | 'Roboto',
40 | 'Helvetica Neue',
41 | 'Arial',
42 | 'Noto Sans',
43 | 'sans-serif',
44 | 'Apple Color Emoji',
45 | 'Segoe UI Emoji',
46 | 'Segoe UI Symbol',
47 | 'Noto Color Emoji'
48 | ]
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/admin/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | {
4 | "source": "/(.*)",
5 | "destination": "/"
6 | }
7 | ]
8 | }
--------------------------------------------------------------------------------
/admin/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server:{port:5174}
8 | })
9 |
--------------------------------------------------------------------------------
/backend/.env.example:
--------------------------------------------------------------------------------
1 | PORT=4000
2 | MONGODB_URI=mongodb://localhost:27017/buildestate
3 | JWT_SECRET=your_jwt_secret_here
4 | EMAIL=your_email_for_notifications
5 | PASSWORD=your_email_password
6 | AZURE_API_KEY=your_azure_ai_key
7 | FIRECRAWL_API_KEY=your_firecrawl_api_key
8 | NODE_ENV=development
9 | IMAGEKIT_PUBLIC_KEY=your_imagekit_public_key
10 | IMAGEKIT_PRIVATE_KEY=your_imagekit_private_key
11 | IMAGEKIT_URL_ENDPOINT=your_imagekit_url_endpoint
12 | ADMIN_EMAIL=admin@buildestate.com
13 | ADMIN_PASSWORD=admin123
14 | WEBSITE_URL=http://localhost:5173
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | # Node.js
2 | node_modules/
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 |
7 | # Logs
8 | logs/
9 | *.log
10 | *.log.*
11 |
12 | # Dependency directories
13 | node_modules/
14 | jspm_packages/
15 |
16 | # Optional npm cache directory
17 | .npm
18 |
19 | # Optional eslint cache
20 | .eslintcache
21 |
22 | # Optional REPL history
23 | .node_repl_history
24 |
25 | # dotenv environment variables file
26 | .env
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
32 | # MacOS
33 | .DS_Store
34 |
35 | # Editor directories and files
36 | .idea/
37 | .vscode/
38 | *.suo
39 | *.ntvs*
40 | *.njsproj
41 | *.sln
42 | *.sw?
43 |
44 | # Build directories
45 | build/
46 | dist/
47 |
48 | # Coverage directory used by tools like istanbul
49 | coverage/
50 |
51 | # Miscellaneous
52 | *.pem
53 | *.p12
54 | *.key
55 | *.crt
56 | *.csr
57 | *.pfx
58 |
59 | # React Native
60 | .expo/
61 | .expo-shared/
62 |
63 | # Parcel-bundler cache (https://parceljs.org/)
64 | .cache
65 | .parcel-cache
66 |
67 | # SvelteKit build / generated files
68 | .svelte-kit
69 |
70 | # Storybook files
71 | out/
72 | .storybook-out/
--------------------------------------------------------------------------------
/backend/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use the official Node.js image as the base image
2 | FROM node:18
3 |
4 | # Set the working directory
5 | WORKDIR /app
6 |
7 | # Copy package.json and package-lock.json
8 | COPY package.json package-lock.json ./
9 |
10 | # Install dependencies
11 | RUN npm install
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Expose the port the app runs on
17 | EXPOSE 4000
18 |
19 | # Start the application
20 | CMD ["node", "server.js"]
--------------------------------------------------------------------------------
/backend/config/config.js:
--------------------------------------------------------------------------------
1 | import dotenv from 'dotenv';
2 | dotenv.config({ path: './.env.local' });
3 |
4 | export const config = {
5 | port: process.env.PORT || 3000,
6 | firecrawlApiKey: process.env.FIRECRAWL_API_KEY,
7 | huggingfaceApiKey: process.env.HUGGINGFACE_API_KEY,
8 | modelId: process.env.MODEL_ID || 'mistralai/Mistral-7B-Instruct-v0.2',
9 | openRouterApiKey: process.env.OPENROUTER_API_KEY,
10 | openAIApiKey: process.env.OPENAI_API_KEY,
11 | azureApiKey: process.env.AZURE_API_KEY,
12 | useAzure: process.env.USE_AZURE === 'true',
13 | };
14 |
15 |
16 |
--------------------------------------------------------------------------------
/backend/config/imagekit.js:
--------------------------------------------------------------------------------
1 | import ImageKit from 'imagekit';
2 | import dotenv from 'dotenv';
3 |
4 | dotenv.config({ path: './.env.local' });
5 |
6 | const imagekit = new ImageKit({
7 | publicKey: process.env.IMAGEKIT_PUBLIC_KEY,
8 | privateKey: process.env.IMAGEKIT_PRIVATE_KEY,
9 | urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT,
10 | });
11 |
12 | if (!process.env.IMAGEKIT_PUBLIC_KEY) {
13 | throw new Error('Missing IMAGEKIT_PUBLIC_KEY');
14 | }
15 |
16 | if (!process.env.IMAGEKIT_PRIVATE_KEY) {
17 | throw new Error('Missing IMAGEKIT_PRIVATE_KEY');
18 | }
19 |
20 | if (!process.env.IMAGEKIT_URL_ENDPOINT) {
21 | throw new Error('Missing IMAGEKIT_URL_ENDPOINT');
22 | }
23 |
24 | console.log("ImageKit connected successfully!");
25 |
26 | export default imagekit;
--------------------------------------------------------------------------------
/backend/config/mongodb.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 | import dotenv from "dotenv";
3 |
4 | dotenv.config();
5 |
6 | const connectdb = async () => {
7 | try {
8 | const conn = await mongoose.connect(process.env.MONGO_URI, {
9 | serverSelectionTimeoutMS: 5000,
10 | socketTimeoutMS: 45000,
11 | });
12 |
13 | console.log(`MongoDB Connected: ${conn.connection.host}`);
14 |
15 | // Handle connection errors after initial connection
16 | mongoose.connection.on('error', err => {
17 | console.error('MongoDB connection error:', err);
18 | });
19 |
20 | mongoose.connection.on('disconnected', () => {
21 | console.log('MongoDB disconnected');
22 | });
23 |
24 | return conn;
25 | } catch (error) {
26 | console.error(`MongoDB Connection Error: ${error.message}`);
27 | process.exit(1);
28 | }
29 | };
30 |
31 | export default connectdb;
--------------------------------------------------------------------------------
/backend/config/nodemailer.js:
--------------------------------------------------------------------------------
1 | import nodemailer from 'nodemailer';
2 | import dotenv from 'dotenv';
3 |
4 | const transporter = nodemailer.createTransport({
5 | host: 'smtp-relay.brevo.com',
6 | port:587,
7 | auth: {
8 | user: process.env.SMTP_USER,
9 | pass: process.env.SMTP_PASS,
10 | }
11 | })
12 |
13 | export default transporter;
--------------------------------------------------------------------------------
/backend/controller/Usercontroller.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import { Router } from "express";
3 | import bcrypt from "bcryptjs";
4 | import jwt from "jsonwebtoken";
5 | import nodemailer from "nodemailer";
6 | import dotenv from "dotenv";
7 | import validator from "validator";
8 | import crypto from "crypto";
9 | import userModel from "../models/Usermodel.js";
10 | import transporter from "../config/nodemailer.js";
11 | import { getWelcomeTemplate } from "../email.js";
12 | import { getPasswordResetTemplate } from "../email.js";
13 |
14 | const backendurl = process.env.BACKEND_URL;
15 |
16 | const createtoken = (id) => {
17 | return jwt.sign({ id }, process.env.JWT_SECRET, {
18 | expiresIn: "30d",
19 | });
20 | };
21 |
22 | dotenv.config();
23 |
24 | const login = async (req, res) => {
25 | try {
26 | const { email, password } = req.body;
27 | const Registeruser = await userModel.findOne({ email });
28 | if (!Registeruser) {
29 | return res.json({ message: "Email not found", success: false });
30 | }
31 | const isMatch = await bcrypt.compare(password, Registeruser.password);
32 | if (isMatch) {
33 | const token = createtoken(Registeruser._id);
34 | return res.json({ token, user: { name: Registeruser.name, email: Registeruser.email }, success: true });
35 | } else {
36 | return res.json({ message: "Invalid password", success: false });
37 | }
38 | } catch (error) {
39 | console.error(error);
40 | res.json({ message: "Server error", success: false });
41 | }
42 | };
43 |
44 | const register = async (req, res) => {
45 | try {
46 | const { name, email, password } = req.body;
47 | if (!validator.isEmail(email)) {
48 | return res.json({ message: "Invalid email", success: false });
49 | }
50 | const hashedPassword = await bcrypt.hash(password, 10);
51 | const newUser = new userModel({ name, email, password: hashedPassword });
52 | await newUser.save();
53 | const token = createtoken(newUser._id);
54 |
55 | // send email
56 | const mailOptions = {
57 | from: process.env.EMAIL,
58 | to: email,
59 | subject: "Welcome to BuildEstate - Your Account Has Been Created",
60 | html: getWelcomeTemplate(name)
61 | };
62 |
63 | await transporter.sendMail(mailOptions);
64 |
65 | return res.json({ token, user: { name: newUser.name, email: newUser.email }, success: true });
66 | } catch (error) {
67 | console.error(error);
68 | return res.json({ message: "Server error", success: false });
69 | }
70 | };
71 |
72 | const forgotpassword = async (req, res) => {
73 | try {
74 | const { email } = req.body;
75 | const user = await userModel.findOne({ email });
76 | if (!user) {
77 | return res.status(404).json({ message: "Email not found", success: false });
78 | }
79 | const resetToken = crypto.randomBytes(20).toString("hex");
80 | user.resetToken = resetToken;
81 | user.resetTokenExpire = Date.now() + 10 * 60 * 1000; // 1 hour
82 | await user.save();
83 | const resetUrl = `${process.env.WEBSITE_URL}/reset/${resetToken}`;
84 | const mailOptions = {
85 | from: process.env.EMAIL,
86 | to: email,
87 | subject: "Password Reset - BuildEstate Security",
88 | html: getPasswordResetTemplate(resetUrl)
89 | };
90 |
91 | await transporter.sendMail(mailOptions);
92 | return res.status(200).json({ message: "Email sent", success: true });
93 | } catch (error) {
94 | console.error(error);
95 | return res.status(500).json({ message: "Server error", success: false });
96 | }
97 | };
98 |
99 | const resetpassword = async (req, res) => {
100 | try {
101 | const { token } = req.params;
102 | const { password } = req.body;
103 | const user = await userModel.findOne({
104 | resetToken: token,
105 | resetTokenExpire: { $gt: Date.now() },
106 | });
107 | if (!user) {
108 | return res.status(400).json({ message: "Invalid or expired token", success: false });
109 | }
110 | user.password = await bcrypt.hash(password, 10);
111 | user.resetToken = undefined;
112 | user.resetTokenExpire = undefined;
113 | await user.save();
114 | return res.status(200).json({ message: "Password reset successful", success: true });
115 | } catch (error) {
116 | console.error(error);
117 | return res.status(500).json({ message: "Server error", success: false });
118 | }
119 | };
120 |
121 | const adminlogin = async (req, res) => {
122 | try {
123 | const { email, password } = req.body;
124 |
125 | if (email === process.env.ADMIN_EMAIL && password === process.env.ADMIN_PASSWORD) {
126 | const token = jwt.sign({ email }, process.env.JWT_SECRET, { expiresIn: '1h' });
127 | return res.json({ token, success: true });
128 | } else {
129 | return res.status(400).json({ message: "Invalid credentials", success: false });
130 | }
131 | } catch (error) {
132 | console.error(error);
133 | return res.status(500).json({ message: "Server error", success: false });
134 | }
135 | };
136 |
137 | const logout = async (req, res) => {
138 | try {
139 | return res.json({ message: "Logged out", success: true });
140 | } catch (error) {
141 | console.error(error);
142 | return res.json({ message: "Server error", success: false });
143 | }
144 | };
145 |
146 | // get name and email
147 |
148 | const getname = async (req, res) => {
149 | try {
150 | const user = await userModel.findById(req.user.id).select("-password");
151 | return res.json(user);
152 | }
153 | catch (error) {
154 | console.error(error);
155 | return res.json({ message: "Server error", success: false });
156 | }
157 | }
158 |
159 |
160 |
161 | export { login, register, forgotpassword, resetpassword, adminlogin, logout, getname };
--------------------------------------------------------------------------------
/backend/controller/formcontroller.js:
--------------------------------------------------------------------------------
1 | import Form from '../models/formmodel.js';
2 |
3 | export const submitForm = async (req, res) => {
4 | try {
5 | const { name, email, phone, message } = req.body; // Debugging log
6 |
7 | const newForm = new Form({
8 | name,
9 | email,
10 | phone,
11 | message,
12 | });
13 |
14 | const savedForm = await newForm.save();
15 |
16 |
17 | res.json({ message: 'Form submitted successfully' });
18 | } catch (error) {
19 | console.error('Error saving form data:', error);
20 | res.status(500).json({ message: 'Server error' });
21 | }
22 | };
--------------------------------------------------------------------------------
/backend/controller/newscontroller.js:
--------------------------------------------------------------------------------
1 | import News from "../models/newsmodel.js";
2 | import nodemailer from "nodemailer";
3 | import dotenv from "dotenv";
4 | import transporter from "../config/nodemailer.js";
5 | import { getEmailTemplate } from "../email.js";
6 |
7 | const submitNewsletter = async (req, res) => {
8 | try {
9 | const { email } = req.body;
10 |
11 | const newNewsletter = new News({
12 | email,
13 | });
14 |
15 | const savedNewsletter = await newNewsletter.save();
16 |
17 | const mailOptions = {
18 | from: process.env.EMAIL,
19 | to: email,
20 | subject: "Welcome to BuildEstate Newsletter! 🏠",
21 | html: getNewsletterTemplate(email),
22 | };
23 |
24 | await transporter.sendMail(mailOptions);
25 |
26 | res.json({ message: "Newsletter submitted successfully" });
27 | } catch (error) {
28 | console.error("Error saving newsletter data:", error);
29 | res.status(500).json({ message: "Server error" });
30 | }
31 | };
32 |
33 | export { submitNewsletter };
34 |
--------------------------------------------------------------------------------
/backend/controller/productcontroller.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import imagekit from "../config/imagekit.js";
3 | import Property from "../models/propertymodel.js";
4 |
5 | const addproperty = async (req, res) => {
6 | try {
7 | const { title, location, price, beds, baths, sqft, type, availability, description, amenities,phone } = req.body;
8 |
9 | const image1 = req.files.image1 && req.files.image1[0];
10 | const image2 = req.files.image2 && req.files.image2[0];
11 | const image3 = req.files.image3 && req.files.image3[0];
12 | const image4 = req.files.image4 && req.files.image4[0];
13 |
14 | const images = [image1, image2, image3, image4].filter((item) => item !== undefined);
15 |
16 | // Upload images to ImageKit and delete after upload
17 | const imageUrls = await Promise.all(
18 | images.map(async (item) => {
19 | const result = await imagekit.upload({
20 | file: fs.readFileSync(item.path),
21 | fileName: item.originalname,
22 | folder: "Property",
23 | });
24 | fs.unlink(item.path, (err) => {
25 | if (err) console.log("Error deleting the file: ", err);
26 | });
27 | return result.url;
28 | })
29 | );
30 |
31 | // Create a new product
32 | const product = new Property({
33 | title,
34 | location,
35 | price,
36 | beds,
37 | baths,
38 | sqft,
39 | type,
40 | availability,
41 | description,
42 | amenities,
43 | image: imageUrls,
44 | phone
45 | });
46 |
47 | // Save the product to the database
48 | await product.save();
49 |
50 | res.json({ message: "Product added successfully", success: true });
51 | } catch (error) {
52 | console.log("Error adding product: ", error);
53 | res.status(500).json({ message: "Server Error", success: false });
54 | }
55 | };
56 |
57 | const listproperty = async (req, res) => {
58 | try {
59 | const property = await Property.find();
60 | res.json({ property, success: true });
61 | } catch (error) {
62 | console.log("Error listing products: ", error);
63 | res.status(500).json({ message: "Server Error", success: false });
64 | }
65 | };
66 |
67 | const removeproperty = async (req, res) => {
68 | try {
69 | const property = await Property.findByIdAndDelete(req.body.id);
70 | if (!property) {
71 | return res.status(404).json({ message: "Property not found", success: false });
72 | }
73 | return res.json({ message: "Property removed successfully", success: true });
74 | } catch (error) {
75 | console.log("Error removing product: ", error);
76 | return res.status(500).json({ message: "Server Error", success: false });
77 | }
78 | };
79 |
80 | const updateproperty = async (req, res) => {
81 | try {
82 | const { id, title, location, price, beds, baths, sqft, type, availability, description, amenities,phone } = req.body;
83 |
84 | const property = await Property.findById(id);
85 | if (!property) {
86 | console.log("Property not found with ID:", id); // Debugging line
87 | return res.status(404).json({ message: "Property not found", success: false });
88 | }
89 |
90 | if (!req.files) {
91 | // No new images provided
92 | property.title = title;
93 | property.location = location;
94 | property.price = price;
95 | property.beds = beds;
96 | property.baths = baths;
97 | property.sqft = sqft;
98 | property.type = type;
99 | property.availability = availability;
100 | property.description = description;
101 | property.amenities = amenities;
102 | property.phone = phone;
103 | // Keep existing images
104 | await property.save();
105 | return res.json({ message: "Property updated successfully", success: true });
106 | }
107 |
108 | const image1 = req.files.image1 && req.files.image1[0];
109 | const image2 = req.files.image2 && req.files.image2[0];
110 | const image3 = req.files.image3 && req.files.image3[0];
111 | const image4 = req.files.image4 && req.files.image4[0];
112 |
113 | const images = [image1, image2, image3, image4].filter((item) => item !== undefined);
114 |
115 | // Upload images to ImageKit and delete after upload
116 | const imageUrls = await Promise.all(
117 | images.map(async (item) => {
118 | const result = await imagekit.upload({
119 | file: fs.readFileSync(item.path),
120 | fileName: item.originalname,
121 | folder: "Property",
122 | });
123 | fs.unlink(item.path, (err) => {
124 | if (err) console.log("Error deleting the file: ", err);
125 | });
126 | return result.url;
127 | })
128 | );
129 |
130 | property.title = title;
131 | property.location = location;
132 | property.price = price;
133 | property.beds = beds;
134 | property.baths = baths;
135 | property.sqft = sqft;
136 | property.type = type;
137 | property.availability = availability;
138 | property.description = description;
139 | property.amenities = amenities;
140 | property.image = imageUrls;
141 | property.phone = phone;
142 |
143 | await property.save();
144 | res.json({ message: "Property updated successfully", success: true });
145 | } catch (error) {
146 | console.log("Error updating product: ", error);
147 | res.status(500).json({ message: "Server Error", success: false });
148 | }
149 | };
150 |
151 | const singleproperty = async (req, res) => {
152 | try {
153 | const { id } = req.params;
154 | const property = await Property.findById(id);
155 | if (!property) {
156 | return res.status(404).json({ message: "Property not found", success: false });
157 | }
158 | res.json({ property, success: true });
159 | } catch (error) {
160 | console.log("Error fetching property:", error);
161 | res.status(500).json({ message: "Server Error", success: false });
162 | }
163 | };
164 |
165 | export { addproperty, listproperty, removeproperty, updateproperty , singleproperty};
--------------------------------------------------------------------------------
/backend/controller/propertyController.js:
--------------------------------------------------------------------------------
1 | import firecrawlService from '../services/firecrawlService.js';
2 | import aiService from '../services/aiService.js';
3 |
4 | export const searchProperties = async (req, res) => {
5 | try {
6 | const { city, maxPrice, propertyCategory, propertyType, limit = 6 } = req.body;
7 |
8 | if (!city || !maxPrice) {
9 | return res.status(400).json({ success: false, message: 'City and maxPrice are required' });
10 | }
11 |
12 | // Extract property data using Firecrawl, specifying the limit
13 | const propertiesData = await firecrawlService.findProperties(
14 | city,
15 | maxPrice,
16 | propertyCategory || 'Residential',
17 | propertyType || 'Flat',
18 | Math.min(limit, 6) // Limit to max 6 properties
19 | );
20 |
21 | // Analyze the properties using AI
22 | const analysis = await aiService.analyzeProperties(
23 | propertiesData.properties,
24 | city,
25 | maxPrice,
26 | propertyCategory || 'Residential',
27 | propertyType || 'Flat'
28 | );
29 |
30 | res.json({
31 | success: true,
32 | properties: propertiesData.properties,
33 | analysis
34 | });
35 | } catch (error) {
36 | console.error('Error searching properties:', error);
37 | res.status(500).json({
38 | success: false,
39 | message: 'Failed to search properties',
40 | error: error.message
41 | });
42 | }
43 | };
44 |
45 | export const getLocationTrends = async (req, res) => {
46 | try {
47 | const { city } = req.params;
48 | const { limit = 5 } = req.query;
49 |
50 | if (!city) {
51 | return res.status(400).json({ success: false, message: 'City parameter is required' });
52 | }
53 |
54 | // Extract location trend data using Firecrawl, with limit
55 | const locationsData = await firecrawlService.getLocationTrends(city, Math.min(limit, 5));
56 |
57 | // Analyze the location trends using AI
58 | const analysis = await aiService.analyzeLocationTrends(
59 | locationsData.locations,
60 | city
61 | );
62 |
63 | res.json({
64 | success: true,
65 | locations: locationsData.locations,
66 | analysis
67 | });
68 | } catch (error) {
69 | console.error('Error getting location trends:', error);
70 | res.status(500).json({
71 | success: false,
72 | message: 'Failed to get location trends',
73 | error: error.message
74 | });
75 | }
76 | };
--------------------------------------------------------------------------------
/backend/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 |
3 | services:
4 | backend:
5 | build: ./backend
6 | ports:
7 | - "4000:4000"
8 | env_file:
9 | - ./backend/.env.local
10 | volumes:
11 | - ./backend:/app
--------------------------------------------------------------------------------
/backend/middleware/authmiddleware.js:
--------------------------------------------------------------------------------
1 | import jwt from "jsonwebtoken";
2 | import userModel from "../models/Usermodel.js";
3 |
4 | export const protect = async (req, res, next) => {
5 | try {
6 | const token = req.headers.authorization?.split(" ")[1];
7 |
8 | if (!token) {
9 | return res.status(401).json({
10 | success: false,
11 | message: "Please login to continue",
12 | });
13 | }
14 |
15 | const decoded = jwt.verify(token, process.env.JWT_SECRET);
16 | const user = await userModel.findById(decoded.id).select("-password");
17 |
18 | if (!user) {
19 | return res.status(401).json({
20 | success: false,
21 | message: "User not found",
22 | });
23 | }
24 |
25 | req.user = user;
26 | next();
27 | } catch (error) {
28 | console.error("Auth error:", error);
29 | return res.status(401).json({
30 | success: false,
31 | message: "Not authorized",
32 | });
33 | }
34 | };
35 |
36 | // In backend/middleware/authmiddleware.js
37 | export const checkAppointmentOwnership = async (req, res, next) => {
38 | try {
39 | const appointment = await Appointment.findById(req.params.id);
40 | if (!appointment) {
41 | return res.status(404).json({
42 | success: false,
43 | message: "Appointment not found",
44 | });
45 | }
46 |
47 | if (appointment.userId.toString() !== req.user._id.toString()) {
48 | return res.status(403).json({
49 | success: false,
50 | message: "Not authorized to access this appointment",
51 | });
52 | }
53 |
54 | req.appointment = appointment;
55 | next();
56 | } catch (error) {
57 | console.error("Error checking appointment ownership:", error);
58 | res.status(500).json({
59 | success: false,
60 | message: "Error checking appointment ownership",
61 | });
62 | }
63 | };
64 |
65 | export default protect;
66 |
--------------------------------------------------------------------------------
/backend/middleware/multer.js:
--------------------------------------------------------------------------------
1 | import multer from "multer";
2 |
3 | const storage = multer.diskStorage({
4 | destination: (req, file, cb) => {
5 | cb(null, "uploads/"); // Ensure the folder exists
6 | },
7 | filename: (req, file, cb) => {
8 | cb(null, Date.now() + "-" + file.originalname); // Unique file names
9 | },
10 | });
11 |
12 | const upload = multer({ storage });
13 | export default upload;
14 |
--------------------------------------------------------------------------------
/backend/middleware/statsMiddleware.js:
--------------------------------------------------------------------------------
1 | import Stats from '../models/statsModel.js';
2 |
3 | export const trackAPIStats = async (req, res, next) => {
4 | const start = Date.now();
5 |
6 | res.on('finish', async () => {
7 | try {
8 | // Skip tracking for OPTIONS and HEAD requests
9 | if (!['OPTIONS', 'HEAD'].includes(req.method)) {
10 | const duration = Date.now() - start;
11 | await Stats.create({
12 | endpoint: req.originalUrl,
13 | method: req.method,
14 | responseTime: duration,
15 | statusCode: res.statusCode
16 | });
17 | }
18 | } catch (error) {
19 | // Log error but don't crash the app
20 | console.error('Error tracking API stats:', error);
21 | }
22 | });
23 |
24 | next();
25 | };
--------------------------------------------------------------------------------
/backend/models/Usermodel.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 | import bcrypt from 'bcryptjs';
3 |
4 | const UserSchema = new mongoose.Schema({
5 | name: { type: String, required: true },
6 | email: { type: String, required: true, unique: true },
7 | password: { type: String, required: true },
8 | resetToken: { type: String },
9 | resetTokenExpire: { type: Date }
10 | });
11 |
12 | const User = mongoose.model('User', UserSchema);
13 |
14 | export default User;
--------------------------------------------------------------------------------
/backend/models/appointmentModel.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 |
3 | const appointmentSchema = new mongoose.Schema({
4 | propertyId: {
5 | type: mongoose.Schema.Types.ObjectId,
6 | ref: 'Property',
7 | required: true
8 | },
9 | userId: {
10 | type: mongoose.Schema.Types.ObjectId,
11 | ref: 'User',
12 | required: true
13 | },
14 | date: {
15 | type: Date,
16 | required: true
17 | },
18 | time: {
19 | type: String,
20 | required: true
21 | },
22 | status: {
23 | type: String,
24 | enum: ['pending', 'confirmed', 'cancelled', 'completed'],
25 | default: 'pending'
26 | },
27 | meetingLink: {
28 | type: String,
29 | trim: true
30 | },
31 | meetingPlatform: {
32 | type: String,
33 | enum: ['zoom', 'google-meet', 'teams', 'other'],
34 | default: 'other'
35 | },
36 | notes: {
37 | type: String
38 | },
39 | cancelReason: {
40 | type: String
41 | },
42 | reminderSent: {
43 | type: Boolean,
44 | default: false
45 | },
46 | feedback: {
47 | rating: {
48 | type: Number,
49 | min: 1,
50 | max: 5
51 | },
52 | comment: String
53 | }
54 | }, {
55 | timestamps: true
56 | });
57 |
58 | // Add indexes for better query performance
59 | appointmentSchema.index({ userId: 1, date: -1 });
60 | appointmentSchema.index({ propertyId: 1, date: -1 });
61 | appointmentSchema.index({ status: 1 });
62 |
63 | const Appointment = mongoose.model('Appointment', appointmentSchema);
64 |
65 | export default Appointment;
--------------------------------------------------------------------------------
/backend/models/formmodel.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 |
3 | const formSchema = new mongoose.Schema({
4 | name: {
5 | type: String,
6 | required: true,
7 | },
8 | email: {
9 | type: String,
10 | required: true,
11 | },
12 | phone: {
13 | type: String,
14 | },
15 | message: {
16 | type: String,
17 | required: true,
18 | },
19 | }, {
20 | timestamps: true,
21 | });
22 |
23 | const Form = mongoose.model('Form', formSchema);
24 |
25 | export default Form;
--------------------------------------------------------------------------------
/backend/models/newsmodel.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const newsSchema = new mongoose.Schema({
4 | email: {
5 | type: String,
6 | required: true,
7 | },
8 | });
9 |
10 | const News = mongoose.model('News', newsSchema);
11 |
12 | export default News;
--------------------------------------------------------------------------------
/backend/models/propertymodel.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const propertySchema = new mongoose.Schema({
4 | title: {
5 | type: String,
6 | required: true,
7 | },
8 | location: {
9 | type: String,
10 | required: true,
11 | },
12 | price: {
13 | type: Number,
14 | required: true,
15 | },
16 | image: {
17 | type: [String],
18 | required: true
19 | },
20 | beds: {
21 | type: Number,
22 | required: true,
23 | },
24 | baths: {
25 | type: Number,
26 | required: true,
27 | },
28 | sqft: {
29 | type: Number,
30 | required: true,
31 | },
32 | type: {
33 | type: String,
34 | required: true,
35 | },
36 | availability: {
37 | type: String,
38 | required: true,
39 | },
40 | description: {
41 | type: String,
42 | required: true,
43 | },
44 | amenities: {
45 | type: Array,
46 | required: true,
47 | },
48 | phone: {
49 | type: String,
50 | required: true,
51 | },
52 | });
53 |
54 | const Property = mongoose.model("Property", propertySchema);
55 |
56 | export default Property;
57 |
--------------------------------------------------------------------------------
/backend/models/statsModel.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 |
3 | const statsSchema = new mongoose.Schema({
4 | endpoint: {
5 | type: String,
6 | required: true
7 | },
8 | method: {
9 | type: String,
10 | required: true,
11 | enum: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'] // Added HEAD
12 | },
13 | timestamp: {
14 | type: Date,
15 | default: Date.now
16 | },
17 | responseTime: {
18 | type: Number,
19 | required: true
20 | },
21 | statusCode: {
22 | type: Number,
23 | required: true
24 | }
25 | }, { timestamps: true });
26 |
27 | // Index for better query performance
28 | statsSchema.index({ endpoint: 1, timestamp: -1 });
29 | statsSchema.index({ method: 1 });
30 | statsSchema.index({ statusCode: 1 });
31 |
32 | const Stats = mongoose.model('Stats', statsSchema);
33 |
34 | export default Stats;
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend",
3 | "version": "1.0.0",
4 | "description": "BuildEstate real estate platform backend API",
5 | "type": "module",
6 | "main": "server.js",
7 | "scripts": {
8 | "start": "node server.js",
9 | "dev": "nodemon server.js",
10 | "build": "npm install",
11 | "render-build": "npm install && npm run build"
12 | },
13 | "author": "Aayush Vaghela",
14 | "license": "ISC",
15 | "dependencies": {
16 | "@azure-rest/ai-inference": "^1.0.0-beta.5",
17 | "@azure/core-auth": "^1.9.0",
18 | "@azure/core-sse": "^2.1.3",
19 | "@google/generative-ai": "^0.23.0",
20 | "@mendable/firecrawl-js": "^1.19.0",
21 | "bcrypt": "^5.1.1",
22 | "bcryptjs": "^2.4.3",
23 | "body-parser": "^1.20.3",
24 | "compression": "^1.8.0",
25 | "cookie-parser": "^1.4.7",
26 | "cors": "^2.8.5",
27 | "crypto": "^1.0.1",
28 | "dotenv": "^16.4.7",
29 | "express": "^4.21.2",
30 | "express-rate-limit": "^7.5.0",
31 | "helmet": "^8.0.0",
32 | "imagekit": "^6.0.0",
33 | "jsonwebtoken": "^9.0.2",
34 | "mongoose": "^8.9.3",
35 | "multer": "^1.4.5-lts.1",
36 | "nodemailer": "^6.9.16",
37 | "nodemon": "^3.1.9",
38 | "transporter": "^0.0.1",
39 | "validator": "^13.12.0"
40 | }
41 | }
--------------------------------------------------------------------------------
/backend/render.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | - type: web
3 | name: real-estate-backend
4 | env: node
5 | buildCommand: npm run render-build
6 | startCommand: npm start
7 | envVars:
8 | - key: NODE_ENV
9 | value: production
--------------------------------------------------------------------------------
/backend/routes/ProductRouter.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { addproperty, listproperty, removeproperty, updateproperty,singleproperty } from '../controller/productcontroller.js';
3 | import upload from '../middleware/multer.js';
4 |
5 | const propertyrouter = express.Router();
6 |
7 | propertyrouter.post('/add', upload.fields([
8 | { name: "image1", maxCount: 1 },
9 | { name: "image2", maxCount: 1 },
10 | { name: "image3", maxCount: 1 },
11 | { name: "image4", maxCount: 1 },
12 | ]), addproperty);
13 | propertyrouter.get('/list', listproperty);
14 | propertyrouter.post('/remove', removeproperty);
15 | propertyrouter.post('/update', upload.fields([
16 | { name: "image1", maxCount: 1 },
17 | { name: "image2", maxCount: 1 },
18 | { name: "image3", maxCount: 1 },
19 | { name: "image4", maxCount: 1 },
20 | ]), updateproperty);
21 | propertyrouter.get('/single/:id', singleproperty);
22 |
23 | export default propertyrouter;
--------------------------------------------------------------------------------
/backend/routes/UserRoute.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { login, register, forgotpassword,adminlogin,resetpassword,getname } from '../controller/Usercontroller.js';
3 | import authMiddleware from '../middleware/authmiddleware.js';
4 |
5 |
6 | const userrouter = express.Router();
7 |
8 | userrouter.post('/login', login);
9 | userrouter.post('/register', register);
10 | userrouter.post('/forgot', forgotpassword);
11 | userrouter.post('/reset/:token', resetpassword);
12 | userrouter.post('/admin', adminlogin);
13 | userrouter.get('/me', authMiddleware, getname);
14 |
15 | export default userrouter;
--------------------------------------------------------------------------------
/backend/routes/adminRoute.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import {
3 | getAdminStats,
4 | getAllAppointments,
5 | updateAppointmentStatus
6 | } from '../controller/adminController.js';
7 |
8 | const router = express.Router();
9 |
10 | router.get('/stats', getAdminStats);
11 | router.get('/appointments',getAllAppointments);
12 | router.put('/appointments/status',updateAppointmentStatus);
13 |
14 | export default router;
--------------------------------------------------------------------------------
/backend/routes/appointmentRoute.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import { protect } from '../middleware/authmiddleware.js';
3 | import {
4 | scheduleViewing,
5 | getAllAppointments,
6 | updateAppointmentStatus,
7 | getAppointmentsByUser,
8 | cancelAppointment,
9 | updateAppointmentMeetingLink,
10 | getAppointmentStats,
11 | submitAppointmentFeedback,
12 | getUpcomingAppointments
13 | } from "../controller/appointmentController.js";
14 |
15 |
16 | const router = express.Router();
17 |
18 | // User routes
19 | router.post("/schedule", protect, scheduleViewing); // Add protect middleware
20 | router.get("/user", getAppointmentsByUser);
21 | router.put("/cancel/:id", cancelAppointment);
22 | router.put("/feedback/:id", submitAppointmentFeedback);
23 | router.get("/upcoming", getUpcomingAppointments);
24 |
25 | // Admin routes
26 | router.get("/all", getAllAppointments);
27 | router.get("/stats", getAppointmentStats);
28 | router.put("/status", updateAppointmentStatus);
29 | router.put("/update-meeting", updateAppointmentMeetingLink);
30 |
31 | export default router;
--------------------------------------------------------------------------------
/backend/routes/formrouter.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { submitForm } from '../controller/formcontroller.js';
3 |
4 | const router = express.Router();
5 |
6 | router.post('/submit', submitForm);
7 |
8 | export default router;
--------------------------------------------------------------------------------
/backend/routes/newsRoute.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { submitNewsletter } from '../controller/newscontroller.js';
3 |
4 | const newsrouter = express.Router();
5 |
6 | newsrouter.post('/newsdata', submitNewsletter);
7 |
8 | export default newsrouter;
--------------------------------------------------------------------------------
/backend/routes/propertyRoutes.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { searchProperties, getLocationTrends } from '../controller/propertyController.js';
3 |
4 | const router = express.Router();
5 |
6 | // Route to search for properties
7 | router.post('/properties/search', searchProperties);
8 |
9 | // Route to get location trends
10 | router.get('/locations/:city/trends', getLocationTrends);
11 |
12 | export default router;
--------------------------------------------------------------------------------
/backend/server.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import dotenv from 'dotenv';
3 | import cors from 'cors';
4 | import rateLimit from 'express-rate-limit';
5 | import helmet from 'helmet';
6 | import compression from 'compression';
7 | import connectdb from './config/mongodb.js';
8 | import { trackAPIStats } from './middleware/statsMiddleware.js';
9 | import propertyrouter from './routes/ProductRouter.js';
10 | import userrouter from './routes/UserRoute.js';
11 | import formrouter from './routes/formrouter.js';
12 | import newsrouter from './routes/newsRoute.js';
13 | import appointmentRouter from './routes/appointmentRoute.js';
14 | import adminRouter from './routes/adminRoute.js';
15 | import propertyRoutes from './routes/propertyRoutes.js';
16 |
17 | dotenv.config();
18 |
19 | const app = express();
20 |
21 | // Rate limiting to prevent abuse
22 | const limiter = rateLimit({
23 | windowMs: 15 * 60 * 1000, // 15 minutes
24 | max: 500, // Limit each IP to 500 requests per window
25 | standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
26 | legacyHeaders: false, // Disable the `X-RateLimit-*` headers
27 | message: { success: false, message: 'Too many requests, please try again later.' }
28 | });
29 |
30 | // Security middlewares
31 | app.use(limiter);
32 | app.use(helmet());
33 | app.use(compression());
34 |
35 | // Middleware
36 | app.use(express.json({ limit: '50mb' }));
37 | app.use(express.urlencoded({ extended: true, limit: '50mb' }));
38 | app.use(trackAPIStats);
39 |
40 |
41 | // CORS Configuration
42 | app.use(cors({
43 | origin: [
44 | 'http://localhost:4000',
45 | 'http://localhost:5174',
46 | 'http://localhost:5173',
47 | 'https://buildestate.vercel.app',
48 | 'https://real-estate-website-admin.onrender.com',
49 | 'https://real-estate-website-backend-zfu7.onrender.com',
50 | ],
51 | credentials: true,
52 | methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'], // Added HEAD
53 | allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
54 | }));
55 |
56 | // Database connection
57 | connectdb().then(() => {
58 | console.log('Database connected successfully');
59 | }).catch(err => {
60 | console.error('Database connection error:', err);
61 | });
62 |
63 |
64 | // API Routes
65 | app.use('/api/products', propertyrouter);
66 | app.use('/api/users', userrouter);
67 | app.use('/api/forms', formrouter);
68 | app.use('/api/news', newsrouter);
69 | app.use('/api/appointments', appointmentRouter);
70 | app.use('/api/admin', adminRouter);
71 | app.use('/api', propertyRoutes);
72 |
73 |
74 | app.use((err, req, res, next) => {
75 | console.error('Error:', err);
76 | const statusCode = err.status || 500;
77 | res.status(statusCode).json({
78 | success: false,
79 | message: err.message || 'Internal server error',
80 | statusCode,
81 | ...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
82 | timestamp: new Date().toISOString()
83 | });
84 | });
85 |
86 |
87 | // Handle unhandled rejections
88 | process.on('unhandledRejection', (err) => {
89 | console.log('UNHANDLED REJECTION! 💥 Shutting down...');
90 | console.error(err);
91 | process.exit(1);
92 | });
93 |
94 | // Status check endpoint
95 | app.get('/status', (req, res) => {
96 | res.status(200).json({ status: 'OK', time: new Date().toISOString() });
97 | });
98 |
99 | // Root endpoint - health check HTML
100 | app.get("/", (req, res) => {
101 | res.send(`
102 |
103 |
104 |
105 |
106 |
107 | BuildEstate API Status
108 |
116 |
117 |
118 |
119 |
BuildEstate API
120 |
Status: Online
121 |
Server Time: ${new Date().toLocaleString()}
122 |
123 |
124 |
The BuildEstate API is running properly. This backend serves property listings, user authentication,
125 | and AI analysis features for the BuildEstate property platform.
126 |
127 |
128 |
131 |
132 |
133 |
134 | `);
135 | });
136 |
137 | const port = process.env.PORT || 4000;
138 |
139 | // Start server
140 | if (process.env.NODE_ENV !== 'test') {
141 | app.listen(port, '0.0.0.0', () => {
142 | console.log(`Server running on port ${port}`);
143 | });
144 | }
145 |
146 | export default app;
--------------------------------------------------------------------------------
/backend/services/aiService.js:
--------------------------------------------------------------------------------
1 | import { config } from "../config/config.js";
2 | import ModelClient, { isUnexpected } from "@azure-rest/ai-inference";
3 | import { AzureKeyCredential } from "@azure/core-auth";
4 |
5 | class AIService {
6 | constructor() {
7 | this.azureApiKey = config.azureApiKey;
8 | }
9 |
10 | async generateText(prompt) {
11 | return this.generateTextWithAzure(prompt);
12 | }
13 |
14 | async generateTextWithAzure(prompt) {
15 | try {
16 | console.log(`Starting Azure AI generation at ${new Date().toISOString()}`);
17 | const startTime = Date.now();
18 |
19 | const client = ModelClient(
20 | "https://models.inference.ai.azure.com",
21 | new AzureKeyCredential(this.azureApiKey)
22 | );
23 |
24 | const response = await client.path("/chat/completions").post({
25 | body: {
26 | messages: [
27 | {
28 | role: "system",
29 | content: "You are an AI real estate expert assistant that provides concise, accurate analysis of property data."
30 | },
31 | {
32 | role: "user",
33 | content: prompt
34 | }
35 | ],
36 | model: "gpt-4o",
37 | temperature: 0.7,
38 | max_tokens: 800,
39 | top_p: 1
40 | }
41 | });
42 |
43 | const endTime = Date.now();
44 | console.log(`Azure AI generation completed in ${(endTime - startTime) / 1000} seconds`);
45 |
46 | if (isUnexpected(response)) {
47 | throw new Error(response.body.error.message || "Azure API error");
48 | }
49 |
50 | return response.body.choices[0].message.content;
51 | } catch (error) {
52 | console.error("Error generating text with Azure:", error);
53 | return `Error: ${error.message}`;
54 | }
55 | }
56 |
57 | // Helper method to filter and clean property data before analysis
58 | _preparePropertyData(properties, maxProperties = 3) {
59 | // Limit the number of properties
60 | const limitedProperties = properties.slice(0, maxProperties);
61 |
62 | // Clean and simplify each property
63 | return limitedProperties.map(property => ({
64 | building_name: property.building_name,
65 | property_type: property.property_type,
66 | location_address: property.location_address,
67 | price: property.price,
68 | area_sqft: property.area_sqft,
69 | // Extract just a few key amenities
70 | amenities: Array.isArray(property.amenities)
71 | ? property.amenities.slice(0, 5)
72 | : [],
73 | // Truncate description to save tokens
74 | description: property.description
75 | ? property.description.substring(0, 150) + (property.description.length > 150 ? '...' : '')
76 | : ''
77 | }));
78 | }
79 |
80 | // Helper method to filter and clean location data
81 | _prepareLocationData(locations, maxLocations = 5) {
82 | // Limit the number of locations
83 | return locations.slice(0, maxLocations);
84 | }
85 |
86 | async analyzeProperties(
87 | properties,
88 | city,
89 | maxPrice,
90 | propertyCategory,
91 | propertyType
92 | ) {
93 | // Prepare limited and cleaned property data
94 | const preparedProperties = this._preparePropertyData(properties);
95 |
96 | const prompt = `As a real estate expert, analyze these properties:
97 |
98 | Properties Found in ${city}:
99 | ${JSON.stringify(preparedProperties, null, 2)}
100 |
101 | INSTRUCTIONS:
102 | 1. Focus ONLY on these properties that match:
103 | - Property Category: ${propertyCategory}
104 | - Property Type: ${propertyType}
105 | - Maximum Price: ${maxPrice} crores
106 | 2. Provide a brief analysis with these sections:
107 | - Property Overview (basic facts about each)
108 | - Best Value Analysis (which offers the best value)
109 | - Quick Recommendations
110 |
111 | Keep your response concise and focused on these properties only.
112 | `;
113 |
114 | return this.generateText(prompt);
115 | }
116 |
117 | async analyzeLocationTrends(locations, city) {
118 | // Prepare limited location data
119 | const preparedLocations = this._prepareLocationData(locations);
120 |
121 | const prompt = `As a real estate expert, analyze these location price trends for ${city}:
122 |
123 | ${JSON.stringify(preparedLocations, null, 2)}
124 |
125 | Please provide:
126 | 1. A brief summary of price trends for each location
127 | 2. Which areas are showing the highest appreciation
128 | 3. Which areas offer the best rental yield
129 | 4. Quick investment recommendations based on this data
130 |
131 | Keep your response concise (maximum 300 words).
132 | `;
133 |
134 | return this.generateText(prompt);
135 | }
136 | }
137 |
138 | export default new AIService();
--------------------------------------------------------------------------------
/backend/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [
4 | {
5 | "src": "server.js",
6 | "use": "@vercel/node"
7 | }
8 | ],
9 | "rewrites": [
10 | {
11 | "source": "/api/(.*)",
12 | "destination": "/server.js"
13 | },
14 | {
15 | "source": "/(.*)",
16 | "destination": "/server.js"
17 | }
18 | ],
19 | "headers": [
20 | {
21 | "source": "/(.*)",
22 | "headers": [
23 | { "key": "Access-Control-Allow-Origin", "value": "*" },
24 | { "key": "Access-Control-Allow-Methods", "value": "GET,POST,PUT,DELETE,OPTIONS,HEAD" },
25 | { "key": "Access-Control-Allow-Headers", "value": "X-Requested-With, Content-Type, Authorization" },
26 | { "key": "Access-Control-Allow-Credentials", "value": "true" }
27 | ]
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/frontend/.env.example:
--------------------------------------------------------------------------------
1 | VITE_API_BASE_URL=http://localhost:4000
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Node.js
2 | node_modules/
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 |
7 | # Logs
8 | logs/
9 | *.log
10 | *.log.*
11 |
12 | # Dependency directories
13 | node_modules/
14 | jspm_packages/
15 |
16 | # Optional npm cache directory
17 | .npm
18 |
19 | # Optional eslint cache
20 | .eslintcache
21 |
22 | # Optional REPL history
23 | .node_repl_history
24 |
25 | # dotenv environment variables file
26 | .env
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
32 | # MacOS
33 | .DS_Store
34 |
35 | # Editor directories and files
36 | .idea/
37 | .vscode/
38 | *.suo
39 | *.ntvs*
40 | *.njsproj
41 | *.sln
42 | *.sw?
43 |
44 | # Build directories
45 | build/
46 | dist/
47 |
48 | # Coverage directory used by tools like istanbul
49 | coverage/
50 |
51 | # Miscellaneous
52 | *.pem
53 | *.p12
54 | *.key
55 | *.crt
56 | *.csr
57 | *.pfx
58 |
59 | # React Native
60 | .expo/
61 | .expo-shared/
62 |
63 | # Parcel-bundler cache (https://parceljs.org/)
64 | .cache
65 | .parcel-cache
66 |
67 | # SvelteKit build / generated files
68 | .svelte-kit
69 |
70 | # Storybook files
71 | out/
72 | .storybook-out/
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AAYUSH412/Real-Estate-Website/24a68ddd4502b6680132ab32ee6b25eac5844a95/frontend/README.md
--------------------------------------------------------------------------------
/frontend/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import react from 'eslint-plugin-react'
4 | import reactHooks from 'eslint-plugin-react-hooks'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 |
7 | export default [
8 | { ignores: ['dist'] },
9 | {
10 | files: ['**/*.{js,jsx}'],
11 | languageOptions: {
12 | ecmaVersion: 2020,
13 | globals: globals.browser,
14 | parserOptions: {
15 | ecmaVersion: 'latest',
16 | ecmaFeatures: { jsx: true },
17 | sourceType: 'module',
18 | },
19 | },
20 | settings: { react: { version: '18.3' } },
21 | plugins: {
22 | react,
23 | 'react-hooks': reactHooks,
24 | 'react-refresh': reactRefresh,
25 | },
26 | rules: {
27 | ...js.configs.recommended.rules,
28 | ...react.configs.recommended.rules,
29 | ...react.configs['jsx-runtime'].rules,
30 | ...reactHooks.configs.recommended.rules,
31 | 'react/jsx-no-target-blank': 'off',
32 | 'react-refresh/only-export-components': [
33 | 'warn',
34 | { allowConstantExport: true },
35 | ],
36 | },
37 | },
38 | ]
39 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | BuildEstate - Premium Real Estate Platform | Find Your Perfect Home
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "buildestate-frontend",
3 | "private": true,
4 | "version": "1.0.0",
5 | "type": "module",
6 | "main": "App.jsx",
7 | "description": "BuildEstate - Premium Real Estate Platform User Interface",
8 | "author": "Aayush Vaghela",
9 | "license": "MIT",
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/AAYUSH412/Real-Estate-Website"
13 | },
14 | "scripts": {
15 | "dev": "vite",
16 | "build": "vite build",
17 | "lint": "eslint .",
18 | "preview": "vite preview"
19 | },
20 | "dependencies": {
21 | "@chakra-ui/react": "^3.2.4",
22 | "@emotion/react": "^11.14.0",
23 | "@emotion/styled": "^11.14.0",
24 | "@heroicons/react": "^2.2.0",
25 | "@radix-ui/react-select": "^2.1.4",
26 | "@radix-ui/react-slot": "^1.1.1",
27 | "@react-spring/web": "^9.7.5",
28 | "@shadcn/ui": "^0.0.4",
29 | "@tailwindcss/line-clamp": "^0.4.4",
30 | "axios": "^1.7.9",
31 | "class-variance-authority": "^0.7.1",
32 | "clsx": "^2.1.1",
33 | "framer-motion": "^11.18.2",
34 | "js-cookie": "^3.0.5",
35 | "lucide-react": "^0.469.0",
36 | "next": "^15.1.4",
37 | "prop-types": "^15.8.1",
38 | "react": "^18.3.1",
39 | "react-dom": "^18.3.1",
40 | "react-helmet-async": "^2.0.5",
41 | "react-icons": "^5.4.0",
42 | "react-markdown": "^10.1.0",
43 | "react-router": "^7.1.1",
44 | "react-router-dom": "^7.1.5",
45 | "react-text-gradients": "^1.0.2",
46 | "react-toastify": "^11.0.3",
47 | "remark-gfm": "^4.0.1",
48 | "tailwind-merge": "^2.6.0",
49 | "tailwindcss-animate": "^1.0.7"
50 | },
51 | "devDependencies": {
52 | "@eslint/js": "^9.17.0",
53 | "@types/node": "^22.10.3",
54 | "@types/react": "^18.3.18",
55 | "@types/react-dom": "^18.3.5",
56 | "@vitejs/plugin-react": "^4.3.4",
57 | "autoprefixer": "^10.4.20",
58 | "eslint": "^9.17.0",
59 | "eslint-plugin-react": "^7.37.2",
60 | "eslint-plugin-react-hooks": "^5.0.0",
61 | "eslint-plugin-react-refresh": "^0.4.16",
62 | "globals": "^15.14.0",
63 | "postcss": "^8.4.49",
64 | "tailwindcss": "^3.4.17",
65 | "vite": "^6.0.5"
66 | }
67 | }
--------------------------------------------------------------------------------
/frontend/postcss.config.js:
--------------------------------------------------------------------------------
1 | // postcss.config.js
2 | export default {
3 | plugins: {
4 | tailwindcss: {},
5 | autoprefixer: {},
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 |
4 | Sitemap: https://buildestate.vercel.app/sitemap.xml
5 |
6 | # Prevent access to sensitive routes
7 | Disallow: /reset/
8 | Disallow: /forgot-password/
--------------------------------------------------------------------------------
/frontend/public/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | https://buildestate.vercel.app/
5 | 2024-03-15
6 | weekly
7 | 1.0
8 |
9 |
10 | https://buildestate.vercel.app/properties
11 | 2024-03-15
12 | daily
13 | 0.9
14 |
15 |
16 | https://buildestate.vercel.app/ai-property-hub
17 | 2024-03-15
18 | weekly
19 | 0.8
20 |
21 |
22 | https://buildestate.vercel.app/about
23 | 2024-03-15
24 | monthly
25 | 0.7
26 |
27 |
28 | https://buildestate.vercel.app/contact
29 | 2024-03-15
30 | monthly
31 | 0.7
32 |
33 |
34 | https://buildestate.vercel.app/login
35 | 2024-03-15
36 | monthly
37 | 0.6
38 |
39 |
40 | https://buildestate.vercel.app/signup
41 | 2024-03-15
42 | monthly
43 | 0.6
44 |
45 |
--------------------------------------------------------------------------------
/frontend/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
3 | import { ToastContainer } from 'react-toastify';
4 | import { HelmetProvider } from 'react-helmet-async';
5 | import Navbar from './components/Navbar'
6 | import Home from './pages/Home'
7 | import Properties from './pages/Properties'
8 | import PropertyDetails from './components/properties/propertydetail';
9 | import Aboutus from './pages/About'
10 | import Contact from './pages/Contact'
11 | import Login from './components/login';
12 | import Signup from './components/signup';
13 | import ForgotPassword from './components/forgetpassword';
14 | import ResetPassword from './components/resetpassword';
15 | import Footer from './components/footer';
16 | import NotFoundPage from './components/Notfound';
17 | import { AuthProvider } from './context/AuthContext';
18 | import AIPropertyHub from './pages/Aiagent'
19 | import StructuredData from './components/SEO/StructuredData';
20 | import 'react-toastify/dist/ReactToastify.css';
21 |
22 |
23 | export const Backendurl = import.meta.env.VITE_API_BASE_URL;
24 |
25 | const App = () => {
26 | return (
27 |
28 |
29 |
30 | {/* Base website structured data */}
31 |
32 |
33 |
34 |
35 |
36 | } />
37 | } />
38 | } />
39 | } />
40 | } />
41 | } />
42 | } />
43 | } />
44 | } />
45 | } />
46 | } />
47 |
48 |
49 |
50 |
51 |
52 |
53 | )
54 | }
55 |
56 | export default App
--------------------------------------------------------------------------------
/frontend/src/assets/Amazon_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
15 |
19 |
22 |
25 |
28 |
31 |
35 |
38 |
39 |
--------------------------------------------------------------------------------
/frontend/src/assets/Google_2015_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/frontend/src/assets/airbnb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/blogdata.js:
--------------------------------------------------------------------------------
1 | export const blogPosts = [
2 | {
3 | id: 1,
4 | title: "First-Time Home Buyer's Guide",
5 | excerpt: "Everything you need to know about purchasing your first home, from financing to closing.",
6 | date: "March 15, 2024",
7 | image: "https://images.unsplash.com/photo-1560518883-ce09059eeffa?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80",
8 | link:"https://www.investopedia.com/updates/first-time-home-buyer"
9 | },
10 | {
11 | id: 2,
12 | title: "Current Real Estate Market Trends",
13 | excerpt: "Analysis of the latest market trends and what they mean for buyers and sellers.",
14 | date: "March 12, 2024",
15 | image: "https://images.unsplash.com/photo-1460472178825-e5240623afd5?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80",
16 | link:"https://www.adanirealty.com/blogs/top-real-estate-trends-that-will-rule-the-market?srsltid=AfmBOoqLph_b9jN_EHz70HRCMbNuxS2DCxEFilEVbVSXE4SI2J7yhpHU"
17 | },
18 | {
19 | id: 3,
20 | title: "Essential Moving Day Tips",
21 | excerpt: "Make your moving day stress-free with these professional tips and tricks.",
22 | date: "March 10, 2024",
23 | image: "https://images.unsplash.com/photo-1600585152220-90363fe7e115?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80",
24 | link:"https://medium.com/@justmiaing/first-time-moving-what-to-get-first-essentials-and-more-tips-7c01169c6066"
25 | }
26 | ];
--------------------------------------------------------------------------------
/frontend/src/assets/data/teammemberdata.js:
--------------------------------------------------------------------------------
1 | import nakulimage from '../images/nakul.png';
2 | import aayushimage from '../images/aayush.png';
3 |
4 | export const teamMembers = [
5 | {
6 | name: 'Nakul Lagad',
7 | position: 'Founder & CEO',
8 | bio: 'With 15+ years in real estate, Nakul leads our vision of transforming property search.',
9 | image: nakulimage,
10 | social: {
11 | linkedin: '#',
12 | twitter: '#',
13 | instagram: '#',
14 | },
15 | },
16 | {
17 | name: 'Aayush Vaghela',
18 | position: 'Chief Technology Officer',
19 | bio: "Tech innovator driving our platform's cutting-edge solutions.",
20 | image: aayushimage,
21 | social: {
22 | linkedin: '#',
23 | twitter: '#',
24 | },
25 | }
26 | ];
--------------------------------------------------------------------------------
/frontend/src/assets/featuredata.js:
--------------------------------------------------------------------------------
1 | import { MessageSquare, Shield, Home, Users } from 'lucide-react';
2 |
3 | export const features = [
4 | {
5 | icon: MessageSquare,
6 | title: 'Direct Communication',
7 | description: 'Get instant responses from our experienced agents through our real-time chat system.',
8 | },
9 | {
10 | icon: Shield,
11 | title: 'Verified Properties',
12 | description: 'Every property is thoroughly inspected and verified to ensure quality and authenticity.',
13 | },
14 | {
15 | icon: Home,
16 | title: 'Quality First',
17 | description: 'We maintain high standards for all properties, ensuring you get the best value.',
18 | },
19 | {
20 | icon: Users,
21 | title: 'Family Focused',
22 | description: 'Find homes that perfectly match your family needs and lifestyle preferences.',
23 | },
24 | ];
--------------------------------------------------------------------------------
/frontend/src/assets/home-regular-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AAYUSH412/Real-Estate-Website/24a68ddd4502b6680132ab32ee6b25eac5844a95/frontend/src/assets/home-regular-24.png
--------------------------------------------------------------------------------
/frontend/src/assets/images/aayush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AAYUSH412/Real-Estate-Website/24a68ddd4502b6680132ab32ee6b25eac5844a95/frontend/src/assets/images/aayush.png
--------------------------------------------------------------------------------
/frontend/src/assets/images/about_enhanced.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AAYUSH412/Real-Estate-Website/24a68ddd4502b6680132ab32ee6b25eac5844a95/frontend/src/assets/images/about_enhanced.jpg
--------------------------------------------------------------------------------
/frontend/src/assets/images/heroimage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AAYUSH412/Real-Estate-Website/24a68ddd4502b6680132ab32ee6b25eac5844a95/frontend/src/assets/images/heroimage.png
--------------------------------------------------------------------------------
/frontend/src/assets/images/nakul.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AAYUSH412/Real-Estate-Website/24a68ddd4502b6680132ab32ee6b25eac5844a95/frontend/src/assets/images/nakul.png
--------------------------------------------------------------------------------
/frontend/src/assets/logo.js:
--------------------------------------------------------------------------------
1 | import Googlelogo from './Google_2015_logo.svg'
2 | import Bookinglogo from './Booking.com_logo.svg'
3 | import Airbnblogo from './airbnb.svg'
4 | import Microsoftlogo from './microsoft.svg'
5 | import Amazonlogo from './Amazon_logo.svg'
6 |
7 |
8 | export const logos={
9 | Googlelogo,
10 | Bookinglogo,
11 | Airbnblogo,
12 | Microsoftlogo,
13 | Amazonlogo
14 | }
--------------------------------------------------------------------------------
/frontend/src/assets/microsoft.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/properties.js:
--------------------------------------------------------------------------------
1 | export const property = [
2 | {
3 | id: 1,
4 | title: 'Royal Orchid Enclave',
5 | location: 'Bandra West, Mumbai',
6 | price: '12,50,00,000',
7 | image: 'https://images.unsplash.com/photo-1613490493576-7fde63acd811?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
8 | },
9 | {
10 | id: 2,
11 | title: 'Emerald Woods',
12 | location: 'DLF Phase 1, Gurgaon',
13 | price: '6,80,00,000',
14 | image: 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
15 | },
16 | {
17 | id: 3,
18 | title: 'Ashoka Residency',
19 | location: 'Koramangala, Bangalore',
20 | price: '9,50,00,000',
21 | image: 'https://images.unsplash.com/photo-1512917774080-9991f1c4c750?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
22 | },
23 | ];
--------------------------------------------------------------------------------
/frontend/src/assets/stepsdata.js:
--------------------------------------------------------------------------------
1 | import { Search, Calendar, Home, Map, Shield, FileCheck } from 'lucide-react';
2 |
3 | export const steps = [
4 | {
5 | icon: Search,
6 | title: 'Find Your Property',
7 | description: 'Use our AI-powered search to discover properties matching your exact needs. Filter by location, price, amenities and more.',
8 | },
9 | {
10 | icon: Calendar,
11 | title: 'Schedule a Visit',
12 | description: 'Book an in-person viewing or take a virtual 3D tour of your selected properties at your convenience.',
13 | },
14 | {
15 | icon: FileCheck,
16 | title: 'Close the Deal',
17 | description: 'Complete paperwork digitally, get expert guidance from our agents, and secure your dream property with ease.',
18 | },
19 | ];
--------------------------------------------------------------------------------
/frontend/src/assets/testimonialdata.js:
--------------------------------------------------------------------------------
1 | export const testimonials = [
2 | {
3 | id: 1,
4 | name: "Sarah Johnson",
5 | location: "New York, NY",
6 | image: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&q=80",
7 | rating: 5,
8 | text: "Found my dream home through BuildEstate. The process was seamless and their team was incredibly helpful throughout the entire journey."
9 | },
10 | {
11 | id: 2,
12 | name: "Michael Chen",
13 | location: "San Francisco, CA",
14 | image: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&q=80",
15 | rating: 5,
16 | text: "Exceptional service! Their attention to detail and understanding of our needs made house hunting a breeze. They found us the perfect property in a competitive market."
17 | },
18 | {
19 | id: 3,
20 | name: "Emily Rodriguez",
21 | location: "Miami, FL",
22 | image: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&q=80",
23 | rating: 4,
24 | text: "Professional team that goes above and beyond. They helped us find the perfect property within our budget and negotiated a better price than we expected."
25 | },
26 | {
27 | id: 4,
28 | name: "David Wilson",
29 | location: "Chicago, IL",
30 | image: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&q=80",
31 | rating: 5,
32 | text: "BuildEstate's AI property recommendations were spot-on! I found exactly what I was looking for in half the time I expected. Their technology is truly impressive."
33 | },
34 | {
35 | id: 5,
36 | name: "Priya Patel",
37 | location: "Austin, TX",
38 | image: "https://images.unsplash.com/photo-1607746882042-944635dfe10e?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&q=80",
39 | rating: 5,
40 | text: "The virtual tours saved me so much time. I was relocating from another state, and BuildEstate made the entire process stress-free with their detailed property insights."
41 | },
42 | {
43 | id: 6,
44 | name: "James Martinez",
45 | location: "Seattle, WA",
46 | image: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&auto=format&fit=crop&w=256&q=80",
47 | rating: 4,
48 | text: "What impressed me most was how transparent they were about every property. No surprises at all, and their support team was always available to answer my questions."
49 | }
50 | ];
--------------------------------------------------------------------------------
/frontend/src/assets/user_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AAYUSH412/Real-Estate-Website/24a68ddd4502b6680132ab32ee6b25eac5844a95/frontend/src/assets/user_logo.png
--------------------------------------------------------------------------------
/frontend/src/components/Companies.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { logos } from '../assets/logo';
3 | import { motion } from 'framer-motion';
4 |
5 | const Companies = () => {
6 | return (
7 |
8 | {/* Additional Trusted Companies Section */}
9 |
15 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default Companies;
36 |
--------------------------------------------------------------------------------
/frontend/src/components/Features.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { motion } from "framer-motion";
3 | import { ArrowRight } from "lucide-react";
4 | import { features } from "../assets/featuredata";
5 |
6 | // Enhanced animation variants
7 | const containerVariants = {
8 | hidden: { opacity: 0 },
9 | visible: {
10 | opacity: 1,
11 | transition: {
12 | staggerChildren: 0.3,
13 | delayChildren: 0.2,
14 | },
15 | },
16 | };
17 |
18 | const cardVariants = {
19 | hidden: { opacity: 0, y: 30 },
20 | visible: {
21 | opacity: 1,
22 | y: 0,
23 | transition: {
24 | type: "spring",
25 | stiffness: 60,
26 | damping: 12
27 | }
28 | },
29 | };
30 |
31 | const Features = () => {
32 | return (
33 |
34 |
35 | {/* Section Header */}
36 |
43 | Our Strengths
44 | Why Choose Us
45 |
46 |
47 | We're committed to providing exceptional service and finding the
48 | perfect home for you with our innovative approach
49 |
50 |
51 |
52 | {/* Features Grid */}
53 |
60 | {features.map((feature, index) => (
61 |
70 |
71 |
72 |
73 |
74 |
75 | {feature.title}
76 |
77 |
78 |
79 | {feature.description}
80 |
81 |
82 |
87 | Learn more
88 |
89 |
90 | ))}
91 |
92 |
93 | {/* Call to action */}
94 |
101 |
107 | Browse Our Properties
108 |
109 |
110 |
111 |
112 |
113 | );
114 | };
115 |
116 | export default Features;
--------------------------------------------------------------------------------
/frontend/src/components/Notfound.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion } from 'framer-motion';
3 | import { Home, ArrowLeft } from 'lucide-react';
4 | import { Link, useNavigate } from 'react-router-dom';
5 |
6 | export default function NotFoundPage() {
7 | const navigate = useNavigate();
8 |
9 | return (
10 |
11 |
16 |
28 | 404
29 |
30 |
31 |
32 | Oops! Page Not Found
33 |
34 |
35 |
36 | The page you're looking for seems to have moved or doesn't exist.
37 | Let's get you back on track!
38 |
39 |
40 |
41 |
navigate(-1)}
43 | className="flex items-center px-6 py-3 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors"
44 | >
45 |
46 | Go Back
47 |
48 |
49 |
53 |
54 | Return Home
55 |
56 |
57 |
58 |
59 | );
60 | }
--------------------------------------------------------------------------------
/frontend/src/components/SEO/AiHubSEO.jsx:
--------------------------------------------------------------------------------
1 | import { Helmet } from 'react-helmet-async';
2 |
3 | const AiHubSEO = () => {
4 | return (
5 |
6 | AI Property Hub | BuildEstate - Market Trends & Property Analysis
7 |
8 |
9 |
10 | {/* Enhanced social sharing */}
11 |
12 |
13 |
14 |
15 |
16 | {/* Local availability note for crawlers */}
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default AiHubSEO;
--------------------------------------------------------------------------------
/frontend/src/components/SEO/StructuredData.jsx:
--------------------------------------------------------------------------------
1 | import { useLocation } from 'react-router-dom';
2 | import PropTypes from 'prop-types';
3 |
4 | const StructuredData = ({ type, data }) => {
5 | const location = useLocation();
6 | const currentUrl = `https://buildestate.vercel.app${location.pathname}`;
7 |
8 | // Different schema types based on page content
9 | const schemas = {
10 | website: {
11 | '@context': 'https://schema.org',
12 | '@type': 'WebSite',
13 | name: 'BuildEstate',
14 | url: 'https://buildestate.vercel.app',
15 | potentialAction: {
16 | '@type': 'SearchAction',
17 | target: '{search_term_string}',
18 | 'query-input': 'required name=search_term_string'
19 | }
20 | },
21 | organization: {
22 | '@context': 'https://schema.org',
23 | '@type': 'Organization',
24 | name: 'BuildEstate',
25 | url: 'https://buildestate.vercel.app',
26 | logo: 'https://buildestate.vercel.app/logo.png',
27 | sameAs: [
28 | 'https://github.com/AAYUSH412/Real-Estate-Website',
29 | 'https://linkedin.com/in/AAYUSH412'
30 | ]
31 | },
32 | property: {
33 | '@context': 'https://schema.org',
34 | '@type': 'RealEstateListing',
35 | name: data?.title || 'Property Listing',
36 | description: data?.description || 'Property details',
37 | url: currentUrl,
38 | datePosted: data?.createdAt || new Date().toISOString(),
39 | address: {
40 | '@type': 'PostalAddress',
41 | addressLocality: data?.location || 'City',
42 | addressRegion: data?.region || 'Region',
43 | addressCountry: 'IN'
44 | },
45 | price: data?.price ? `₹${data.price}` : '',
46 | floorSize: {
47 | '@type': 'QuantitativeValue',
48 | unitText: 'SQFT',
49 | value: data?.sqft || ''
50 | },
51 | numberOfRooms: data?.beds || '',
52 | numberOfBathroomsTotal: data?.baths || ''
53 | },
54 | aiHub: {
55 | '@context': 'https://schema.org',
56 | '@type': 'WebApplication',
57 | name: 'AI Property Hub',
58 | applicationCategory: 'RealEstateApplication',
59 | description: 'AI-powered real estate analytics and recommendations tool',
60 | url: 'https://buildestate.vercel.app/ai-property-hub',
61 | offers: {
62 | '@type': 'Offer',
63 | price: '0',
64 | priceCurrency: 'INR',
65 | availability: 'https://schema.org/InStock'
66 | }
67 | }
68 | };
69 |
70 | const schemaData = schemas[type] || schemas.website;
71 |
72 | return (
73 |
77 | );
78 | };
79 |
80 | StructuredData.propTypes = {
81 | type: PropTypes.string.isRequired,
82 | data: PropTypes.object
83 | };
84 |
85 |
86 |
87 | export default StructuredData;
--------------------------------------------------------------------------------
/frontend/src/components/aboutus/Benefit.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion } from 'framer-motion';
3 | import { Home, Globe, Headphones, List } from 'lucide-react';
4 |
5 | const benefits = [
6 | {
7 | icon: Home,
8 | title: 'Verified Properties',
9 | description: 'Every property is thoroughly verified for quality and security.',
10 | },
11 | {
12 | icon: Globe,
13 | title: 'User-Friendly Platform',
14 | description: 'Intuitive navigation and seamless property management.',
15 | },
16 | {
17 | icon: Headphones,
18 | title: '24/7 Support',
19 | description: 'Round-the-clock assistance for all your queries.',
20 | },
21 | {
22 | icon: List,
23 | title: 'Comprehensive Listings',
24 | description: 'Wide range of properties to match every need and budget.',
25 | },
26 | ];
27 |
28 | export default function Benefits() {
29 | return (
30 |
31 |
32 |
38 | Why Choose BuildEstate?
39 |
40 |
41 | Experience the difference with our comprehensive property solutions
42 |
43 |
44 |
45 |
46 | {benefits.map((benefit, index) => {
47 | const Icon = benefit.icon;
48 | return (
49 |
58 |
59 |
60 |
61 | {benefit.title}
62 | {benefit.description}
63 |
64 | );
65 | })}
66 |
67 |
68 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/frontend/src/components/aboutus/Contup.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from "react";
2 | import { useInView, useMotionValue, useSpring } from "framer-motion";
3 |
4 | export default function CountUp({
5 | to,
6 | from = 0,
7 | direction = "up",
8 | delay = 0,
9 | duration = 1,
10 | className = "",
11 | startWhen = true,
12 | separator = "",
13 | onStart,
14 | onEnd,
15 | }) {
16 | const ref = useRef(null);
17 | const motionValue = useMotionValue(direction === "down" ? to : from);
18 |
19 | const damping = 20 + 40 * (1 / duration);
20 | const stiffness = 100 * (1 / duration);
21 |
22 | const springValue = useSpring(motionValue, {
23 | damping,
24 | stiffness,
25 | });
26 |
27 | const isInView = useInView(ref, { once: true, margin: "0px" });
28 |
29 | useEffect(() => {
30 | if (ref.current) {
31 | ref.current.textContent = String(direction === "down" ? to : from);
32 | }
33 | }, [from, to, direction]);
34 |
35 | useEffect(() => {
36 | if (isInView && startWhen) {
37 | if (typeof onStart === "function") {
38 | onStart();
39 | }
40 |
41 | const timeoutId = setTimeout(() => {
42 | motionValue.set(direction === "down" ? from : to);
43 | }, delay * 1000);
44 |
45 | const durationTimeoutId = setTimeout(() => {
46 | if (typeof onEnd === "function") {
47 | onEnd();
48 | }
49 | }, delay * 1000 + duration * 1000);
50 |
51 | return () => {
52 | clearTimeout(timeoutId);
53 | clearTimeout(durationTimeoutId);
54 | };
55 | }
56 | }, [isInView, startWhen, motionValue, direction, from, to, delay, onStart, onEnd, duration]);
57 |
58 | useEffect(() => {
59 | const unsubscribe = springValue.on("change", (latest) => {
60 | if (ref.current) {
61 | const options = {
62 | useGrouping: !!separator,
63 | minimumFractionDigits: 0,
64 | maximumFractionDigits: 0,
65 | };
66 |
67 | const formattedNumber = Intl.NumberFormat("en-US", options).format(
68 | latest.toFixed(0)
69 | );
70 |
71 | ref.current.textContent = separator
72 | ? formattedNumber.replace(/,/g, separator)
73 | : formattedNumber;
74 | }
75 | });
76 |
77 | return () => unsubscribe();
78 | }, [springValue, separator]);
79 |
80 | return ;
81 | }
82 |
--------------------------------------------------------------------------------
/frontend/src/components/aboutus/Hero.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion } from 'framer-motion';
3 |
4 | export default function Hero() {
5 | return (
6 |
7 |
8 | {/* Animated gradient background */}
9 |
10 |
21 |
22 | {/* Animated shapes */}
23 |
24 |
33 |
34 |
43 |
44 |
53 |
54 |
55 | {/* Pattern overlay */}
56 |
57 |
58 |
59 | {/* Content */}
60 |
66 |
67 | Building Your Future, One Home at a Time
68 |
69 |
70 | We're more than just a property platform - we're your partner in finding the perfect place to call home.
71 |
72 |
73 | {/* Decorative line */}
74 |
80 |
81 |
82 |
83 | );
84 | }
--------------------------------------------------------------------------------
/frontend/src/components/aboutus/Milestone.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion, useInView, useMotionValue, useSpring } from 'framer-motion';
3 | import { Building2, Home, Target } from 'lucide-react';
4 | import CountUp from './Contup';
5 |
6 | const milestones = [
7 | {
8 | icon: Home,
9 | title: 'Properties Listed',
10 | value: 5000,
11 | description: 'And growing daily',
12 | },
13 | {
14 | icon: Target,
15 | title: 'Happy Clients',
16 | value: 10000,
17 | description: 'Satisfied customers',
18 | },
19 | ];
20 |
21 | export default function Milestones() {
22 | return (
23 |
24 |
25 |
31 | Our Journey So Far
32 |
33 |
34 | Milestones that mark our growth and success
35 |
36 |
37 |
38 |
39 | {milestones.map((milestone, index) => {
40 | const Icon = milestone.icon;
41 | return (
42 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {milestone.title}
58 | {milestone.description}
59 |
60 | );
61 | })}
62 |
63 |
64 |
65 | );
66 | }
--------------------------------------------------------------------------------
/frontend/src/components/aboutus/Mission.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion } from 'framer-motion';
3 | import { Target, Eye } from 'lucide-react';
4 |
5 | export default function MissionVision() {
6 | return (
7 |
8 |
9 |
15 | Our Purpose
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
Our Mission
29 |
30 |
31 | To provide a transparent and efficient property rental experience for all users.
32 | We strive to make the process of finding your perfect home as seamless as possible,
33 | while maintaining the highest standards of service and integrity.
34 |
35 |
36 |
37 |
43 |
44 |
45 |
Our Vision
46 |
47 |
48 | Empowering millions of users to find their perfect home with ease and trust.
49 | We envision a future where property search is not just about finding a place to live,
50 | but about discovering a community to belong to.
51 |
52 |
53 |
54 |
55 |
56 | );
57 | }
--------------------------------------------------------------------------------
/frontend/src/components/aboutus/Team.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { motion } from "framer-motion";
3 | import TeamMember from "./Teammember";
4 | import { teamMembers } from "../../assets/data/teammemberdata";
5 |
6 | export default function Team() {
7 | return (
8 |
9 |
10 |
16 | Meet Our Team
17 |
18 |
19 | The passionate individuals behind BuildEstate's success
20 |
21 |
22 |
23 |
24 | {teamMembers.map((member, index) => (
25 |
34 |
35 |
36 | ))}
37 |
38 |
39 |
40 | );
41 | }
--------------------------------------------------------------------------------
/frontend/src/components/aboutus/Teammember.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Linkedin, Twitter, Instagram } from 'lucide-react';
3 | import { teamMembers } from '../../assets/data/teammemberdata';
4 |
5 | const TeamMember = ({ name, position, bio, image, social }) => {
6 | return (
7 |
8 |
9 |
14 |
{name}
15 |
{position}
16 |
{bio}
17 |
18 | {social.linkedin && (
19 |
20 |
21 |
22 | )}
23 | {social.twitter && (
24 |
25 |
26 |
27 | )}
28 | {social.instagram && (
29 |
30 |
31 |
32 | )}
33 |
34 |
35 |
36 | );
37 | };
38 |
39 | export default TeamMember;
--------------------------------------------------------------------------------
/frontend/src/components/aboutus/Values.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion } from 'framer-motion';
3 | import { Shield, Clock, CheckCircle } from 'lucide-react';
4 |
5 | const values = [
6 | {
7 | icon: Shield,
8 | title: 'Trust',
9 | description: 'We verify all property owners and renters to ensure a safe and reliable experience for everyone.',
10 | },
11 | {
12 | icon: CheckCircle,
13 | title: 'Transparency',
14 | description: 'Clear and honest property listings with accurate information and no hidden fees.',
15 | },
16 | {
17 | icon: Clock,
18 | title: 'Efficiency',
19 | description: 'Streamlined property search and listing process to save you time and effort.',
20 | },
21 | ];
22 |
23 | export default function Values() {
24 | return (
25 |
26 |
27 |
33 | Our Values
34 |
35 |
36 | These core values guide everything we do at BuildEstate
37 |
38 |
39 |
40 |
41 | {values.map((value, index) => {
42 | const Icon = value.icon;
43 | return (
44 |
53 |
54 |
55 |
56 | {value.title}
57 | {value.description}
58 |
59 | );
60 | })}
61 |
62 |
63 |
64 | );
65 | }
--------------------------------------------------------------------------------
/frontend/src/components/ai/Header.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { Link, useNavigate } from 'react-router-dom';
3 | import PropTypes from 'prop-types';
4 | import { motion, AnimatePresence } from 'framer-motion';
5 | import { Brain, ArrowLeft, Menu, X } from 'lucide-react';
6 |
7 | const Header = ({ title = "AI Property Assistant" }) => {
8 | const [isMenuOpen, setIsMenuOpen] = useState(false);
9 | const navigate = useNavigate();
10 |
11 | const toggleMenu = () => {
12 | setIsMenuOpen(!isMenuOpen);
13 | };
14 |
15 | const handleBack = () => {
16 | navigate('/');
17 | };
18 |
19 | return (
20 |
25 |
26 | {/* Logo and Title */}
27 |
28 |
32 |
33 |
34 |
40 |
41 | {title}
42 |
43 |
44 |
45 |
46 | {/* Mobile Title (centered) */}
47 |
48 |
49 | {title.split(' ').slice(0, 2).join(' ')}
50 |
51 |
52 |
53 | {/* Navigation */}
54 |
55 |
61 |
62 | Back Home
63 |
64 |
65 | {/* Mobile Menu Button */}
66 |
72 | {isMenuOpen ? : }
73 |
74 |
75 |
76 |
77 | {/* Mobile Menu */}
78 |
79 | {isMenuOpen && (
80 |
87 |
88 |
93 |
94 | Back to Home
95 |
96 |
97 |
setIsMenuOpen(false)}
100 | className="text-center px-4 py-3 bg-white/10 hover:bg-white/20 rounded-lg text-white transition-colors"
101 | >
102 | Browse Properties
103 |
104 |
105 |
106 | )}
107 |
108 |
109 | );
110 | };
111 |
112 | Header.propTypes = {
113 | title: PropTypes.string,
114 | };
115 |
116 | export default Header;
--------------------------------------------------------------------------------
/frontend/src/components/ai/PropertyCard.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import { motion } from 'framer-motion';
3 | import { Building, MapPin, Maximize, Tag, Plus, ArrowRight } from 'lucide-react';
4 | import { useState } from 'react';
5 |
6 | const PropertyCard = ({ property }) => {
7 | const [isExpanded, setIsExpanded] = useState(false);
8 |
9 | return (
10 |
17 | {/* Header with gradient background */}
18 |
19 |
25 |
26 |
27 |
28 |
29 | {property.building_name}
30 |
31 |
32 |
33 |
34 | {property.location_address}
35 |
36 |
37 |
38 |
39 |
40 | {/* Content area */}
41 |
42 | {/* Price and area information */}
43 |
44 |
45 |
Price
46 |
{property.price}
47 |
48 |
49 | {property.area_sqft && (
50 |
51 |
Area
52 |
53 |
54 |
{property.area_sqft}
55 |
56 |
57 | )}
58 |
59 |
60 | {/* Property description - Collapsible on mobile */}
61 |
62 |
setIsExpanded(!isExpanded)}
64 | className="flex w-full items-center justify-between text-left sm:pointer-events-none"
65 | >
66 |
67 |
68 | Overview
69 |
70 |
75 |
76 |
77 |
78 |
79 |
88 |
89 | {property.description}
90 |
91 |
92 |
93 |
94 | {/* Amenities section */}
95 | {property.amenities && property.amenities.length > 0 && (
96 |
97 |
98 |
99 | Amenities
100 |
101 |
102 | {property.amenities.slice(0, isExpanded ? property.amenities.length : 2).map((amenity, index) => (
103 |
109 | {amenity}
110 |
111 | ))}
112 | {!isExpanded && property.amenities.length > 2 && (
113 |
setIsExpanded(true)}
117 | className="bg-gray-50 text-gray-600 text-xs px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-full flex items-center border border-gray-100 cursor-pointer"
118 | >
119 |
120 | {property.amenities.length - 2} more
121 |
122 | )}
123 |
124 |
125 | )}
126 |
127 |
128 | );
129 | };
130 |
131 | PropertyCard.propTypes = {
132 | property: PropTypes.shape({
133 | building_name: PropTypes.string,
134 | location_address: PropTypes.string,
135 | price: PropTypes.string,
136 | property_type: PropTypes.string,
137 | area_sqft: PropTypes.string,
138 | description: PropTypes.string,
139 | amenities: PropTypes.arrayOf(PropTypes.string)
140 | })
141 | };
142 |
143 | export default PropertyCard;
--------------------------------------------------------------------------------
/frontend/src/components/contact/ContactHero.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion } from 'framer-motion';
3 |
4 | export default function Hero() {
5 | return (
6 |
7 | {/* Animated gradient background */}
8 |
9 |
20 |
21 | {/* Animated shapes */}
22 |
23 |
32 |
33 |
42 |
43 |
44 | {/* Pattern overlay */}
45 |
46 |
47 |
48 | {/* Content Section */}
49 |
55 |
56 | Get in Touch With Us
57 |
58 |
59 | Have questions about our properties? Need assistance with finding your perfect home?
60 | Our team is here to help you every step of the way.
61 |
62 |
63 | {/* Decorative line */}
64 |
70 |
71 |
72 | );
73 | }
--------------------------------------------------------------------------------
/frontend/src/components/contact/ContactInfo.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion } from 'framer-motion';
3 | import { Phone, Mail, MapPin, Clock } from 'lucide-react';
4 | import ContactInfoItem from './InfoItem';
5 |
6 | const contactInfo = [
7 | {
8 | icon: Phone,
9 | title: 'Phone',
10 | content: '+1 (234) 567-890',
11 | link: 'tel:+1234567890',
12 | },
13 | {
14 | icon: Mail,
15 | title: 'Email',
16 | content: 'support@buildestate.com',
17 | link: 'mailto:support@buildestate.com',
18 | },
19 | {
20 | icon: MapPin,
21 | title: 'Address',
22 | content: '123 Main Street, City, Country',
23 | link: '#map',
24 | },
25 | {
26 | icon: Clock,
27 | title: 'Working Hours',
28 | content: 'Mon-Fri: 9 AM - 6 PM',
29 | },
30 | ];
31 |
32 | export default function ContactInfo() {
33 | return (
34 |
40 | Our Office
41 |
42 | {contactInfo.map((info, index) => (
43 |
44 | ))}
45 |
46 |
47 | );
48 | }
--------------------------------------------------------------------------------
/frontend/src/components/contact/Formhandle.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import useContactForm from './useContactform';
3 |
4 | const ContactForm = () => {
5 | const { formData, errors, handleChange, handleSubmit } = useContactForm();
6 |
7 | return (
8 |
57 | );
58 | };
59 |
60 | export default ContactForm;
--------------------------------------------------------------------------------
/frontend/src/components/contact/InfoItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Phone, Mail, MapPin, Clock } from 'lucide-react';
3 |
4 | export default function ContactInfoItem({ icon: Icon, title, content, link }) {
5 | const ContentWrapper = link ? 'a' : 'div';
6 | const props = link ? { href: link } : {};
7 |
8 | return (
9 |
13 |
14 |
15 |
16 |
17 |
{title}
18 |
{content}
19 |
20 |
21 | );
22 | }
--------------------------------------------------------------------------------
/frontend/src/components/contact/contactForm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { motion } from 'framer-motion';
3 | import { Send } from 'lucide-react';
4 | import useContactForm from './useContactform';
5 |
6 | function ContactForm() {
7 | const { formData, errors, handleChange, handleSubmit } = useContactForm();
8 |
9 | return (
10 |
16 | Send Us a Message
17 |
18 |
19 |
20 | Name *
21 |
22 |
32 | {errors.name &&
{errors.name}
}
33 |
34 |
35 |
36 |
37 | Email *
38 |
39 |
49 | {errors.email &&
{errors.email}
}
50 |
51 |
52 |
53 |
54 | Phone Number (Optional)
55 |
56 |
64 |
65 |
66 |
67 |
68 | Message *
69 |
70 |
80 | {errors.message &&
{errors.message}
}
81 |
82 |
83 |
87 |
88 | Send Message
89 |
90 |
91 |
92 | );
93 | }
94 |
95 | export default ContactForm;
--------------------------------------------------------------------------------
/frontend/src/components/contact/useContactform.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import axios from 'axios';
3 | import { toast } from 'react-toastify';
4 | import 'react-toastify/dist/ReactToastify.css';
5 | import { Backendurl } from '../../App';
6 |
7 | export default function useContactForm() {
8 | const [formData, setFormData] = useState({
9 | name: '',
10 | email: '',
11 | phone: '',
12 | message: '',
13 | });
14 |
15 | const [errors, setErrors] = useState({});
16 |
17 | const validateForm = () => {
18 | const newErrors = {};
19 |
20 | if (!formData.name.trim()) {
21 | newErrors.name = 'Name is required';
22 | }
23 |
24 | if (!formData.email.trim()) {
25 | newErrors.email = 'Email is required';
26 | } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
27 | newErrors.email = 'Please enter a valid email address';
28 | }
29 |
30 | if (!formData.message.trim()) {
31 | newErrors.message = 'Message is required';
32 | }
33 |
34 | setErrors(newErrors);
35 | return Object.keys(newErrors).length === 0;
36 | };
37 |
38 | const handleChange = (e) => {
39 | const { name, value } = e.target;
40 | setFormData(prev => ({ ...prev, [name]: value }));
41 | if (errors[name]) {
42 | setErrors(prev => ({ ...prev, [name]: undefined }));
43 | }
44 | };
45 |
46 | const handleSubmit = async (e) => {
47 | e.preventDefault();
48 | if (validateForm()) {
49 | try {
50 | const response = await axios.post(`${Backendurl}/api/forms/submit`, formData);
51 | toast.success('Form submitted successfully!');
52 | // Reset form
53 | setFormData({ name: '', email: '', phone: '', message: '' });
54 | } catch (error) {
55 | toast.error('Error submitting form. Please try again.');
56 | console.error('Error submitting form:', error);
57 | }
58 | } else {
59 | console.log('Validation errors:', errors); // Debugging log
60 | }
61 | };
62 |
63 | return { formData, errors, handleChange, handleSubmit };
64 | }
--------------------------------------------------------------------------------
/frontend/src/components/forgetpassword.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import axios from "axios";
3 | import { Link } from "react-router-dom";
4 | import { motion } from "framer-motion";
5 | import { Mail, ArrowLeft, Loader } from "lucide-react";
6 | import { toast } from "react-toastify";
7 | import { Backendurl } from "../App";
8 |
9 | const ForgotPassword = () => {
10 | const [email, setEmail] = useState("");
11 | const [loading, setLoading] = useState(false);
12 |
13 | const handleSubmit = async (e) => {
14 | e.preventDefault();
15 | setLoading(true);
16 | try {
17 | const response = await axios.post(`${Backendurl}/api/users/forgot`, { email });
18 | if (response.data.success) {
19 | toast.success("Reset link sent to your email!");
20 | } else {
21 | toast.error(response.data.message);
22 | }
23 | } catch (error) {
24 | console.error("Error sending reset email:", error);
25 | toast.error("Failed to send reset link. Please try again.");
26 | } finally {
27 | setLoading(false);
28 | }
29 | };
30 |
31 | return (
32 |
33 |
39 |
40 | {/* Logo & Title */}
41 |
42 |
43 |
44 | BuildEstate
45 |
46 |
47 |
Forgot Password?
48 |
No worries, we'll send you reset instructions.
49 |
50 |
51 |
52 |
53 |
54 | Email address
55 |
56 |
57 |
58 | setEmail(e.target.value)}
65 | className="w-full pl-10 pr-4 py-3 rounded-lg bg-gray-50 border border-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 transition-all duration-200"
66 | placeholder="Enter your email"
67 | />
68 |
69 |
70 |
71 |
76 | {loading ? (
77 |
78 | ) : (
79 | "Send Reset Link"
80 | )}
81 |
82 |
83 |
87 |
88 | Back to login
89 |
90 |
91 |
92 |
93 |
94 | );
95 | };
96 |
97 | export default ForgotPassword;
--------------------------------------------------------------------------------
/frontend/src/components/login.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import axios from "axios";
3 | import { Link, useNavigate } from "react-router-dom";
4 | import { FaEye, FaEyeSlash } from "react-icons/fa";
5 | import { motion } from "framer-motion";
6 | import { Loader } from "lucide-react";
7 | import { Backendurl } from "../App";
8 | import { authStyles } from "../styles/auth";
9 | import { toast } from "react-toastify";
10 | import { useAuth } from '../context/AuthContext';
11 |
12 |
13 | const Login = () => {
14 | const [formData, setFormData] = useState({
15 | email: "",
16 | password: ""
17 | });
18 | const [showPassword, setShowPassword] = useState(false);
19 | const [loading, setLoading] = useState(false);
20 | const navigate = useNavigate();
21 | const { login } = useAuth();
22 |
23 |
24 | const handleChange = (e) => {
25 | const { name, value } = e.target;
26 | setFormData(prev => ({
27 | ...prev,
28 | [name]: value
29 | }));
30 | };
31 |
32 | const handleSubmit = async (e) => {
33 | e.preventDefault();
34 | setLoading(true);
35 | try {
36 | const response = await axios.post(
37 | `${Backendurl}/api/users/login`,
38 | formData
39 | );
40 | if (response.data.success) {
41 | await login(response.data.token, response.data.user);
42 | toast.success("Login successful!");
43 | navigate("/");
44 | } else {
45 | toast.error(response.data.message);
46 | }
47 | } catch (error) {
48 | console.error("Error logging in:", error);
49 | toast.error("An error occurred. Please try again.");
50 | } finally {
51 | setLoading(false);
52 | }
53 | };
54 |
55 | return (
56 |
57 |
63 |
64 | {/* Logo & Title */}
65 |
66 |
67 |
68 | BuildEstate
69 |
70 |
71 |
Welcome back
72 |
Please sign in to your account
73 |
74 |
75 |
76 | {/* Email Field */}
77 |
78 |
79 | Email address
80 |
81 |
91 |
92 |
93 | {/* Password Field */}
94 |
95 |
96 | Password
97 |
98 |
99 |
109 | setShowPassword(!showPassword)}
112 | className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
113 | >
114 | {showPassword ? : }
115 |
116 |
117 |
118 |
119 | {/* Forgot Password Link */}
120 |
121 |
125 | Forgot password?
126 |
127 |
128 |
129 | {/* Submit Button */}
130 |
135 | {loading ? (
136 |
137 | ) : (
138 | "Sign in"
139 | )}
140 |
141 |
142 | {/* Divider */}
143 |
144 |
147 |
148 | Don't have an account?
149 |
150 |
151 |
152 | {/* Sign Up Link */}
153 |
157 | Create an account
158 |
159 |
160 |
161 |
162 |
163 | );
164 | };
165 |
166 | export default Login;
--------------------------------------------------------------------------------
/frontend/src/components/properties/Filtersection.jsx:
--------------------------------------------------------------------------------
1 | import { Home, IndianRupee, Filter } from "lucide-react";
2 | import { motion } from "framer-motion";
3 |
4 | const propertyTypes = ["House", "Apartment", "Villa", "Office"];
5 | const availabilityTypes = ["Rent", "Buy", "Lease"];
6 | const priceRanges = [
7 | { min: 0, max: 5000000, label: "Under ₹50L" },
8 | { min: 5000000, max: 10000000, label: "₹50L - ₹1Cr" },
9 | { min: 10000000, max: 20000000, label: "₹1Cr - ₹2Cr" },
10 | { min: 20000000, max: Number.MAX_SAFE_INTEGER, label: "Above ₹2Cr" }
11 | ];
12 |
13 | const FilterSection = ({ filters, setFilters, onApplyFilters }) => {
14 | const handleChange = (e) => {
15 | const { name, value } = e.target;
16 | setFilters(prev => ({
17 | ...prev,
18 | [name]: value
19 | }));
20 | };
21 |
22 | const handlePriceRangeChange = (min, max) => {
23 | setFilters(prev => ({
24 | ...prev,
25 | priceRange: [min, max]
26 | }));
27 | };
28 |
29 | const handleReset = () => {
30 | setFilters({
31 | propertyType: "",
32 | priceRange: [0, Number.MAX_SAFE_INTEGER],
33 | bedrooms: "0",
34 | bathrooms: "0",
35 | availability: "",
36 | searchQuery: "",
37 | sortBy: ""
38 | });
39 | };
40 |
41 | return (
42 |
48 | {/* Header */}
49 |
50 |
51 |
52 |
Filters
53 |
54 |
58 | Reset All
59 |
60 |
61 |
62 |
63 | {/* Property Type */}
64 |
65 |
66 |
67 | Property Type
68 |
69 |
70 | {propertyTypes.map((type) => (
71 | handleChange({
74 | target: { name: "propertyType", value: type.toLowerCase() }
75 | })}
76 | className={`px-4 py-2 rounded-lg text-sm font-medium transition-all
77 | ${filters.propertyType === type.toLowerCase()
78 | ? "bg-blue-600 text-white"
79 | : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`}
80 | >
81 | {type}
82 |
83 | ))}
84 |
85 |
86 |
87 | {/* Price Range */}
88 |
89 |
90 |
91 | Price Range
92 |
93 |
94 | {priceRanges.map(({ min, max, label }) => (
95 | handlePriceRangeChange(min, max)}
98 | className={`px-4 py-2 rounded-lg text-sm font-medium transition-all
99 | ${filters.priceRange[0] === min && filters.priceRange[1] === max
100 | ? "bg-blue-600 text-white"
101 | : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`}
102 | >
103 | {label}
104 |
105 | ))}
106 |
107 |
108 |
109 | {/* Rest of your existing filter groups */}
110 | {/* ... */}
111 |
112 |
113 | onApplyFilters(filters)}
115 | className="flex-1 bg-blue-600 text-white py-3 rounded-lg hover:bg-blue-700
116 | transition-colors font-medium"
117 | >
118 | Apply Filters
119 |
120 |
121 |
122 |
123 | );
124 | };
125 |
126 | export default FilterSection;
--------------------------------------------------------------------------------
/frontend/src/components/properties/Searchbar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Search, X, MapPin } from 'lucide-react';
3 | import { motion, AnimatePresence } from 'framer-motion';
4 |
5 | const SearchBar = ({ onSearch, className }) => {
6 | const [searchQuery, setSearchQuery] = useState('');
7 | const [recentSearches, setRecentSearches] = useState([]);
8 | const [showSuggestions, setShowSuggestions] = useState(false);
9 |
10 | // Popular locations suggestion
11 | const popularLocations = [
12 | 'Mumbai','Goa','Jaipur','Ahmedabad'
13 | ];
14 |
15 | useEffect(() => {
16 | // Load recent searches from localStorage
17 | const saved = localStorage.getItem('recentSearches');
18 | if (saved) {
19 | setRecentSearches(JSON.parse(saved));
20 | }
21 | }, []);
22 |
23 | const handleSearch = (query) => {
24 | if (!query.trim()) return;
25 |
26 | // Update recent searches
27 | const updatedSearches = [
28 | query,
29 | ...recentSearches.filter(item => item !== query)
30 | ].slice(0, 5);
31 |
32 | setRecentSearches(updatedSearches);
33 | localStorage.setItem('recentSearches', JSON.stringify(updatedSearches));
34 |
35 | onSearch(query);
36 | setShowSuggestions(false);
37 | };
38 |
39 | const handleSubmit = (e) => {
40 | e.preventDefault();
41 | handleSearch(searchQuery);
42 | };
43 |
44 | const clearSearch = () => {
45 | setSearchQuery('');
46 | onSearch('');
47 | };
48 |
49 | const handleKeyDown = (e) => {
50 | if (e.key === 'Escape') {
51 | setShowSuggestions(false);
52 | }
53 | };
54 |
55 | return (
56 |
57 |
58 | setSearchQuery(e.target.value)}
63 | onFocus={() => setShowSuggestions(true)}
64 | onKeyDown={handleKeyDown}
65 | className="w-full pl-12 pr-20 py-3 rounded-lg border border-gray-200
66 | focus:border-blue-500 focus:ring-2 focus:ring-blue-200
67 | transition-all text-gray-800 placeholder-gray-400"
68 | />
69 |
73 |
74 |
75 | {searchQuery && (
76 |
85 |
86 |
87 | )}
88 |
93 |
94 | Search
95 |
96 |
97 |
98 |
99 | {/* Search Suggestions Dropdown */}
100 |
101 | {showSuggestions && (
102 |
109 | {recentSearches.length > 0 && (
110 |
111 |
112 | Recent Searches
113 |
114 | {recentSearches.map((query, index) => (
115 | {
118 | setSearchQuery(query);
119 | handleSearch(query);
120 | }}
121 | className="w-full text-left px-3 py-2 hover:bg-gray-50
122 | rounded-md flex items-center gap-2 text-gray-700"
123 | >
124 |
125 | {query}
126 |
127 | ))}
128 |
129 | )}
130 |
131 |
132 |
133 | Popular Locations
134 |
135 | {popularLocations.map((location, index) => (
136 | {
139 | setSearchQuery(location);
140 | handleSearch(location);
141 | }}
142 | className="w-full text-left px-3 py-2 hover:bg-gray-50
143 | rounded-md flex items-center gap-2 text-gray-700"
144 | >
145 |
146 | {location}
147 |
148 | ))}
149 |
150 |
151 | )}
152 |
153 |
154 | );
155 | };
156 |
157 | export default SearchBar;
--------------------------------------------------------------------------------
/frontend/src/components/resetpassword.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import axios from "axios";
3 | import { useParams, useNavigate, Link } from "react-router-dom";
4 | import { motion } from "framer-motion";
5 | import { Lock, Eye, EyeOff, Loader, ArrowLeft } from "lucide-react";
6 | import { toast } from "react-toastify";
7 | import { Backendurl } from "../App";
8 |
9 | const ResetPassword = () => {
10 | const { token } = useParams();
11 | const [password, setPassword] = useState("");
12 | const [showPassword, setShowPassword] = useState(false);
13 | const [loading, setLoading] = useState(false);
14 | const navigate = useNavigate();
15 |
16 | const handleSubmit = async (e) => {
17 | e.preventDefault();
18 | setLoading(true);
19 | try {
20 | const response = await axios.post(`${Backendurl}/api/users/reset/${token}`, { password });
21 | if (response.data.success) {
22 | toast.success("Password reset successful!");
23 | navigate("/login");
24 | } else {
25 | toast.error(response.data.message);
26 | }
27 | } catch (error) {
28 | console.error("Error resetting password:", error);
29 | toast.error("Failed to reset password. Please try again.");
30 | } finally {
31 | setLoading(false);
32 | }
33 | };
34 |
35 | return (
36 |
37 |
43 |
44 | {/* Logo & Title */}
45 |
46 |
47 |
48 | BuildEstate
49 |
50 |
51 |
Reset Password
52 |
Enter your new password below
53 |
54 |
55 |
56 |
57 |
58 | New Password
59 |
60 |
61 |
62 | setPassword(e.target.value)}
69 | className="w-full pl-10 pr-12 py-3 rounded-lg bg-gray-50 border border-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 transition-all duration-200"
70 | placeholder="Enter new password"
71 | />
72 | setShowPassword(!showPassword)}
75 | className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
76 | >
77 | {showPassword ? : }
78 |
79 |
80 |
81 |
82 |
87 | {loading ? (
88 |
89 | ) : (
90 | "Reset Password"
91 | )}
92 |
93 |
94 |
98 |
99 | Back to login
100 |
101 |
102 |
103 |
104 |
105 | );
106 | };
107 |
108 | export default ResetPassword;
--------------------------------------------------------------------------------
/frontend/src/context/AuthContext.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useState, useContext, useEffect } from "react";
2 | import axios from "axios";
3 | import { Backendurl } from "../App";
4 |
5 | const AuthContext = createContext(null);
6 |
7 | export const AuthProvider = ({ children }) => {
8 | const [isLoggedIn, setIsLoggedIn] = useState(false);
9 | const [user, setUser] = useState(null);
10 | const [loading, setLoading] = useState(true);
11 |
12 | const checkAuthStatus = async () => {
13 | const token = localStorage.getItem("token");
14 | if (token) {
15 | try {
16 | // Add token to axios default headers
17 | axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
18 |
19 | const response = await axios.get(`${Backendurl}/api/users/me`, {
20 | headers: {
21 | Authorization: `Bearer ${token}`,
22 | },
23 | });
24 |
25 | if (response.data) {
26 | setUser(response.data);
27 | setIsLoggedIn(true);
28 | } else {
29 | throw new Error("Invalid response");
30 | }
31 | } catch (error) {
32 | console.error("Auth check failed:", error);
33 | // Only remove token if it's an auth error (401)
34 | if (error.response && error.response.status === 401) {
35 | localStorage.removeItem("token");
36 | setIsLoggedIn(false);
37 | setUser(null);
38 | }
39 | }
40 | }
41 | setLoading(false);
42 | };
43 |
44 | useEffect(() => {
45 | checkAuthStatus();
46 | }, []);
47 |
48 | const login = async (token, userData) => {
49 | localStorage.setItem("token", token);
50 | axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
51 | setIsLoggedIn(true);
52 | setUser(userData);
53 | };
54 |
55 | const logout = () => {
56 | localStorage.removeItem("token");
57 | delete axios.defaults.headers.common["Authorization"];
58 | setIsLoggedIn(false);
59 | setUser(null);
60 | };
61 |
62 | return (
63 |
64 | {children}
65 |
66 | );
67 | };
68 |
69 | export const useAuth = () => useContext(AuthContext);
70 |
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | /* src/index.css */
2 | @tailwind base;
3 | @tailwind components;
4 | @tailwind utilities;
5 |
6 | /* Add to your global CSS or relevant component */
7 |
8 |
9 | .filter-group {
10 | @apply space-y-3;
11 | }
12 |
13 | .filter-label {
14 | @apply flex items-center text-gray-700 font-medium text-sm;
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import './index.css'
4 | import App from './App.jsx'
5 |
6 | createRoot(document.getElementById('root')).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/frontend/src/pages/About.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import Hero from '../components/aboutus/Hero';
3 | import Mission from '../components/aboutus/Mission';
4 | import Values from '../components/aboutus/Values';
5 | import Team from '../components/aboutus/Team';
6 | import Benefits from '../components/aboutus/Benefit';
7 | import Milestones from '../components/aboutus/Milestone';
8 |
9 | const About = () => {
10 | useEffect(() => {
11 | window.scrollTo(0, 0);
12 | }, []);
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default About
27 |
--------------------------------------------------------------------------------
/frontend/src/pages/Contact.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { motion } from 'framer-motion';
3 | import ContactHero from '../components/contact/ContactHero';
4 | import ContactForm from '../components/contact/contactForm';
5 | import ContactInfo from '../components/contact/ContactInfo';
6 |
7 |
8 | const Contact = () => {
9 | return (
10 |
26 | )
27 | }
28 |
29 | export default Contact
30 |
--------------------------------------------------------------------------------
/frontend/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Hero from '../components/Hero'
3 | import Companies from '../components/Companies'
4 | import Features from '../components/Features'
5 | import Properties from '../components/propertiesshow'
6 | import Steps from '../components/Steps'
7 | import Testimonials from '../components/testimonial'
8 | import Blog from '../components/Blog'
9 |
10 | const Home = () => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | )
22 | }
23 |
24 | export default Home
25 |
--------------------------------------------------------------------------------
/frontend/src/pages/Properties.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropertiesPage from '../components/properties/Propertiespage'
3 |
4 | const Properties = () => {
5 | return (
6 |
9 | )
10 | }
11 |
12 | export default Properties
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/frontend/src/services/api.js:
--------------------------------------------------------------------------------
1 | // frontend/src/services/api.js
2 | import axios from 'axios';
3 |
4 | // Change this to match your backend server port (4000)
5 | const API_URL = import.meta.env.VITE_API_BASE_URL;
6 |
7 | const api = axios.create({
8 | baseURL: API_URL,
9 | headers: {
10 | 'Content-Type': 'application/json',
11 | }
12 | });
13 |
14 | export const searchProperties = async (searchParams) => {
15 | try {
16 | const response = await api.post('/api/properties/search', searchParams);
17 | return response.data;
18 | } catch (error) {
19 | console.error('Error searching properties:', error);
20 | throw error;
21 | }
22 | };
23 |
24 | export const getLocationTrends = async (city) => {
25 | try {
26 | const response = await api.get(`/api/locations/${encodeURIComponent(city)}/trends`);
27 | return response.data;
28 | } catch (error) {
29 | console.error('Error fetching location trends:', error);
30 | throw error;
31 | }
32 | };
33 |
34 | export default api;
--------------------------------------------------------------------------------
/frontend/src/styles/auth.js:
--------------------------------------------------------------------------------
1 | export const authStyles = {
2 | input: "bg-gray-50 border border-gray-300 text-gray-900 rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-3 transition-all duration-200",
3 | button: "w-full bg-blue-600 text-white py-3 rounded-lg hover:bg-blue-700 transition-all duration-200 flex items-center justify-center gap-2 font-medium shadow-md",
4 | socialButton: "w-full border border-gray-300 text-gray-700 py-3 rounded-lg hover:bg-gray-50 transition-all duration-200 flex items-center justify-center gap-2",
5 | link: "text-blue-600 hover:text-blue-700 font-medium transition-colors duration-200"
6 | };
--------------------------------------------------------------------------------
/frontend/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | // tailwind.config.js
2 | module.exports = {
3 | theme: {
4 | extend: {
5 | transitionProperty: {
6 | height: "height",
7 | spacing: "margin, padding",
8 | },
9 | backgroundSize: {
10 | 'size-200': '200% 200%',
11 | },
12 | animation: {
13 | 'bg-pos-x': 'bg-pos-x 3s ease infinite',
14 | },
15 | keyframes: {
16 | 'bg-pos-x': {
17 | '0%, 100%': { 'background-position': '0% 50%' },
18 | '50%': { 'background-position': '100% 50%' },
19 | },
20 | },
21 | },
22 | },
23 | content: ["./index.html", "./src/**/*.{js,jsx,ts,tsx}"],
24 | darkMode: "class",
25 | theme: {
26 | extend: {
27 | colors: {
28 | primary: {
29 | 50: "#eff6ff",
30 | 100: "#dbeafe",
31 | 200: "#bfdbfe",
32 | 300: "#93c5fd",
33 | 400: "#60a5fa",
34 | 500: "#3b82f6",
35 | 600: "#2563eb",
36 | 700: "#1d4ed8",
37 | 800: "#1e40af",
38 | 900: "#1e3a8a",
39 | 950: "#172554",
40 | },
41 | },
42 | },
43 | fontFamily: {
44 | body: [
45 | "Inter",
46 | "ui-sans-serif",
47 | "system-ui",
48 | "-apple-system",
49 | "system-ui",
50 | "Segoe UI",
51 | "Roboto",
52 | "Helvetica Neue",
53 | "Arial",
54 | "Noto Sans",
55 | "sans-serif",
56 | "Apple Color Emoji",
57 | "Segoe UI Emoji",
58 | "Segoe UI Symbol",
59 | "Noto Color Emoji",
60 | ],
61 | sans: [
62 | "Inter",
63 | "ui-sans-serif",
64 | "system-ui",
65 | "-apple-system",
66 | "system-ui",
67 | "Segoe UI",
68 | "Roboto",
69 | "Helvetica Neue",
70 | "Arial",
71 | "Noto Sans",
72 | "sans-serif",
73 | "Apple Color Emoji",
74 | "Segoe UI Emoji",
75 | "Segoe UI Symbol",
76 | "Noto Color Emoji",
77 | ],
78 | },
79 | },
80 | };
81 |
--------------------------------------------------------------------------------
/frontend/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | { "source": "/(.*)", "destination": "/index.html" }
4 | ],
5 | "headers": [
6 | {
7 | "source": "/(.*)",
8 | "headers": [
9 | {
10 | "key": "X-Content-Type-Options",
11 | "value": "nosniff"
12 | },
13 | {
14 | "key": "X-Frame-Options",
15 | "value": "DENY"
16 | },
17 | {
18 | "key": "X-XSS-Protection",
19 | "value": "1; mode=block"
20 | },
21 | {
22 | "key": "Referrer-Policy",
23 | "value": "strict-origin-when-cross-origin"
24 | },
25 | {
26 | "key": "Permissions-Policy",
27 | "value": "camera=(), microphone=(), geolocation=(self)"
28 | }
29 | ]
30 | },
31 | {
32 | "source": "/sitemap.xml",
33 | "headers": [
34 | { "key": "Content-Type", "value": "application/xml" },
35 | { "key": "Cache-Control", "value": "public, max-age=86400" }
36 | ]
37 | },
38 | {
39 | "source": "/robots.txt",
40 | "headers": [
41 | { "key": "Content-Type", "value": "text/plain" },
42 | { "key": "Cache-Control", "value": "public, max-age=86400" }
43 | ]
44 | }
45 | ],
46 | "redirects": [
47 | { "source": "/home", "destination": "/", "permanent": true },
48 | { "source": "/properties-listing", "destination": "/properties", "permanent": true },
49 | { "source": "/property/:id", "destination": "/properties/single/:id", "permanent": true },
50 | { "source": "/ai-agent", "destination": "/ai-property-hub", "permanent": true },
51 | { "source": "/contact-us", "destination": "/contact", "permanent": true },
52 | { "source": "/about-us", "destination": "/about", "permanent": true }
53 | ]
54 | }
--------------------------------------------------------------------------------
/frontend/vite.config.js:
--------------------------------------------------------------------------------
1 | import path from "path"
2 | import react from "@vitejs/plugin-react"
3 | import { defineConfig } from "vite"
4 |
5 | export default defineConfig({
6 | plugins: [react()],
7 | server:{port:5173},
8 | resolve: {
9 | alias: {
10 | "@": path.resolve(__dirname, "./src"),
11 | },
12 | },
13 | build: {
14 | rollupOptions: {
15 | external: [],
16 | output: {
17 | manualChunks: (id) => {
18 | // Create a separate chunk for react-helmet-async
19 | if (id.includes('node_modules/react-helmet-async')) {
20 | return 'react-helmet-async';
21 | }
22 | }
23 | }
24 | },
25 | commonjsOptions: {
26 | include: [/node_modules/],
27 | }
28 | },
29 | optimizeDeps: {
30 | include: ['react-helmet-async']
31 | }
32 | })
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "buildestate",
3 | "version": "1.0.0",
4 | "description": "BuildEstate - Premium Real Estate Platform with AI-Powered Insights",
5 | "private": true,
6 | "scripts": {
7 | "setup": "npm install && cd backend && npm install && cd ../frontend && npm install && cd ../admin && npm install",
8 | "dev": "concurrently \"cd backend && npm run dev\" \"cd frontend && npm run dev\" \"cd admin && npm run dev\"",
9 | "build": "cd backend && npm run build && cd ../frontend && npm run build && cd ../admin && npm run build",
10 | "start": "cd backend && npm start",
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "workspaces": [
14 | "backend",
15 | "frontend",
16 | "admin"
17 | ],
18 | "keywords": [
19 | "real-estate",
20 | "property",
21 | "ai",
22 | "react",
23 | "nodejs",
24 | "mongodb"
25 | ],
26 | "author": "Aayush Vaghela",
27 | "license": "MIT",
28 | "repository": {
29 | "type": "git",
30 | "url": "https://github.com/AAYUSH412/Real-Estate-Website.git"
31 | },
32 | "bugs": {
33 | "url": "https://github.com/AAYUSH412/Real-Estate-Website/issues"
34 | },
35 | "homepage": "https://github.com/AAYUSH412/Real-Estate-Website#readme",
36 | "devDependencies": {
37 | "concurrently": "^8.0.0"
38 | }
39 | }
--------------------------------------------------------------------------------