# Level 1 Team Project

### 1. House Real Estate Simulator

This guide is tailored for students who want to build an House Real Estate like application using **React** and **Vite** frameworks, based on the files and designs you've shared.

<figure><img src="/files/7tsypQP6G2rLpQQqAEeh" alt="" width="375"><figcaption></figcaption></figure>

### 2. Initialize the Project

#### 1. Create a new React + Vite project:

```bash
npm create vite@latest haunted-house-app -- --template react
cd haunted-house-app
npm install
```

#### 2. Install dependencies (if you use additional ones like React Router or icons):

### Step 1: Set Up Project Structure

1. Structure your folders as follows:

```
/src
  /components
    HouseCard.jsx
    Footer.jsx
    Message.jsx
  App.jsx
  main.jsx
/style.css
/index.html
```

### Step 2: Create a new component

* Created a new file:\
  `src/components/HouseCard.jsx`
* Moved the JSX code from your original `App.jsx` into this new `HouseCard` component.
* Passed the necessary data as **three props** only:
  * `index`: the current house number.
  * `total`: total number of houses.
  * `houseData`: all house details (price, location, etc.) as an object.

```jsx
// File: components/HouseCard.jsx
import React from "react"

export default function HouseCard({ index, total, houseData }) {
    // Prepare list items dynamically
    const features = [
        { label: "Price", value: houseData.price },
        { label: "Location", value: houseData.location },
        { label: "Square Feet", value: houseData.squareFeet },
        { label: "Acres", value: houseData.acres },
        { label: "Year Built", value: houseData.yearBuilt },
        { label: "Bedrooms", value: houseData.bedrooms },
        { label: "Bathrooms", value: houseData.bathrooms },
        { label: "Other Rooms", value: houseData.otherRooms },
        { label: "Garage", value: houseData.garage ? "Yes" : "No" },
        { label: "Air Conditioning", value: houseData.airConditioning ? "Yes" : "No" },
        { label: "Heating", value: houseData.heating ? "Yes" : "No" },
        { label: "Haunted", value: houseData.haunted ? "Yes" : "No" },
    ]

    return (
        <div className="house-card" key={houseData.id}>
            <p>Listing {index + 1} of {total}</p>
            <img src={houseData.image} alt={`House in ${houseData.location}`} />
            <div>
                <ul>
                    {features.map((item, idx) => (
                        <li key={idx}>
                            <span>{item.label}: {item.value} </span>
                        </li>
                    ))}
                </ul>
            </div>
        </div>
    )
}

```

Create data foler at root directory and add houseForSale.js data

```javascript
// housesForSale.js
import {nanoid} from "nanoid"

export default [
    {
        id: nanoid(),
        image: "./images/eleanor-brooke-62ZrBo3PoKc-unsplash.jpg",
        price: "$1,342,000",
        location: "Podunk, Ohio",
        squareFeet: "3,752",
        acres: 4.7,
        bedrooms: 5,
        bathrooms: 4,
        otherRooms: 6,
        yearBuilt: 1902,
        garage: false,
        airConditioning: false,
        heating: true,
        haunted: true,
    },{
        id: nanoid(),
        image: "./images/luis-muller-t1IcKA8HkUM-unsplash.jpg",
        price: "$152,000",
        location: "Stumpsville, Indiana",
        squareFeet: "1,252",
        acres: 1.4,
        bedrooms: 3,
        bathrooms: 2,
        otherRooms: 2,
        yearBuilt: 1932,
        garage: false,
        airConditioning: true,
        heating: true, 
        haunted: true, 
    },{
        id: nanoid(),
        image: "./images/amber-kipp-DJEkBfLp6bc-unsplash.jpg",
        price: "$233,000",
        location: "Backwater, Tennessee",
        squareFeet: "1,995",
        acres: 2.6,
        bedrooms: 4,
        bathrooms: 4,
        otherRooms: 2,
        yearBuilt: 1877,
        garage: true,
        airConditioning: true,
        heating: true,
        haunted: true,
    },{
        id: nanoid(),
        image: "./images/nathan-dumlao-Mw1JgIAuK6c-unsplash.jpg",
        price: "$176,500",
        location: "Sticksville, Vermont",
        squareFeet: "956",
        acres: 12.6,
        bedrooms: 2,
        bathrooms: 1,
        otherRooms: 1,
        yearBuilt: 1916,
        garage: false,
        airConditioning: false,
        heating: false,
        haunted: true,
    },{
        id: nanoid(),
        image: "./images/robbie-down-3IRIerl16nk-unsplash.jpg",
        price: "$142,000",
        location: "Hinterland, Virginia",
        squareFeet: "1,212",
        acres: 9.4,
        bedrooms: 3,
        bathrooms: 2,
        otherRooms: 1,
        yearBuilt: 1925,
        garage: false,
        airConditioning: false,
        heating: true,
        haunted: true,
    },{
        id: nanoid(),
        image: "./images/peter-herrmann-eZaEWy2rAIc-unsplash.jpg",
        price: "$380,120",
        location: "Backwoods, Oregon",
        squareFeet: "2,612",
        acres: 5.2,
        bedrooms: 5,
        bathrooms: 4,
        otherRooms: 4,
        yearBuilt: 1903,
        garage: true,
        airConditioning: true,
        heating: true,
        haunted: true,
    },
    
]
```

### Step 3: Dynamically generate list items

* Inside `HouseCard`, we use `.map()` to dynamically iterate over an array of objects containing label and value pairs (e.g., Price, Location, etc.).
* No more hard-coded `<li>` elements!

### Step 4: Update App.jsx

* Import `HouseCard` into `App.jsx`.
* Replace the `map()` function's JSX with the new `<HouseCard />` component.
* Pass props neatly.

```jsx
// File: App.jsx
import React from "react"
import housesForSale from "../data/housesForSale"
import HouseCard from "./components/HouseCard"

export default function App() {
    return (
        <div className="wrapper">
            <header>
                <img className="logo" src="images/logo.png" alt="Haunted House Real Estate Logo" />
            </header>
            <div className="house-cards-container">
                {housesForSale.map((houseData, index, array) => (
                    <HouseCard 
                        key={houseData.id}
                        index={index} 
                        total={array.length} 
                        houseData={houseData} 
                    />
                ))}
            </div>
        </div>
    )
}
```

### Step 5: Maintain Visual Style

* Your CSS (style.css) remains the same. So, the **visual output does not change**.
* Design looks exactly like your provided UI screenshot!

```css
* {
    box-sizing: border-box
}


body { 
    margin: 0;
}

header {
    width: 100vw;
    display: flex;
    justify-content: center;
    padding: 15px;
    background: #495057;
    position: fixed;
    box-shadow: 2px 1px 4px 1px #212529;
}

.logo {
    height: 100px;
    border: 1px solid #212529;
    box-shadow: 1px 1px 4px 1px #212529;
    border-radius: 15px;
    background: #dee2e6;
    padding: 0 60px;
}

.wrapper {
    background: hsla(210, 14%, 83%, 1);
    background: linear-gradient(270deg, hsla(210, 14%, 83%, 1) 0%, hsla(208, 7%, 46%, 1) 100%);
    background: -moz-linear-gradient(270deg, hsla(210, 14%, 83%, 1) 0%, hsla(208, 7%, 46%, 1) 100%);
    background: -webkit-linear-gradient(270deg, hsla(210, 14%, 83%, 1) 0%, hsla(208, 7%, 46%, 1) 100%);
    background-attachment: fixed;
}

.house-cards-container {
    padding-top: 150px;
    display: flex;
    justify-content: center;
    width: 100vw;
    flex-wrap: wrap;
    gap: 30px;
    padding-bottom: 30px;
}

.house-card {
    width: 400px;
    height: 760px;
    border-radius: 15px;
    background: #f8f9fa;
    display: flex;
    flex-direction: column;
    padding: 30px;
    border: 2px solid #212529;
    box-shadow: 2px 2px 4px 1px #212529;
}

.house-card p {
    margin: -10px 0 10px 0;
    text-align: center;
    font-weight: 600;
    font-size: 16px;
}

.house-card img {
    width: 340px;
    height: 340px;
    object-fit: cover;
    border-radius: 15px;
    align-self: center;
    margin-bottom: 20px;
    border: 2px solid #212529;
}

.house-card ul {
    display: flex;
    width: 100%;
    flex-wrap: wrap;
    justify-content: space-between;
    height: 250px;
    gap: 20px 60px;
    font-size: 16px;
}

ul {
    margin: 0;
    padding: 0;
}

li {
    list-style: none;
}

li:nth-of-type(even) {
    text-align: end;
}

span {
    font-weight: 700;
    display: block;
}

```

Output:

### Optional (Next Level Enhancements)

If you want to go further:

1. **PropTypes** → Add type checking for props.
2. **Add animations** → Hover effects or transitions.
3. **Responsive layout** → Make it mobile-friendly.
4. **Fetch data dynamically** → From an API or external JSON file.

## <mark style="color:orange;">Challenge</mark>: **Fetch data dynamically** → From an API or external JSON file.

We are now moving toward a **real external property API integration**.\
Let’s go step by step.  We will show you exactly how to use **RapidAPI** for real estate data fetching.

### Step 1: Sign Up and Get API Key on RapidAPI

1. Go to [RapidAPI.com](https://rapidapi.com/).
2. Create an account (free tier is fine). You need a credit card for creating API endpoint.
3. Search for:
   * **Zillow API** *(great for U.S. properties)*
   * **Realtor API** *(good for listings, agents, property photos)*
4. Subscribe to the API.
5. Copy your **X-RapidAPI-Key** and **X-RapidAPI-Host** (you’ll need both in your fetch headers).

### Step 2: Example Code Integration (React + RapidAPI)

Let’s assume you pick **Zillow API** (easy & fast). For secure management of your API key, we need a separate file named <mark style="color:red;">**.env**</mark> at the root directory.  Replace `'YOUR_API_KEY_HERE'` with your actual RapidAPI key.

```jsx
// .env
VITE_API_KEY = <your API Key>
```

Update your `useEffect()` in `App.jsx` like this:

```jsx
// Updated App.jsx
import React, { useEffect, useState } from 'react'
import HouseCard from './components/HouseCardFetch'

export default function App() {
  const [housesForSale, setHousesForSale] = useState([])

  // Use environment variable for API key if available
  const apiKey = import.meta.env.VITE_API_KEY || 'YOUR_API_KEY'

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(
          'https://zillow69.p.rapidapi.com/search?location=Houston%2C%20TX&status_type=ForSale&daysOn=1&soldInLast=1',
          {
            method: 'GET',
            headers: {
              'X-RapidAPI-Key': apiKey,
              'X-RapidAPI-Host': 'zillow69.p.rapidapi.com',
            },
          },
        )
        const data = await response.json()
        console.log(data.props) // Check structure
        setHousesForSale(data.props) // Adjust based on response structure
      } catch (error) {
        console.error('Error fetching houses:', error)
      }
    }

    fetchData()
  }, [])

  return (
    <div className="wrapper">
      <header>
        <img
          className="logo"
          src="images/logo.png"
          alt="Haunted House Real Estate Logo"
        />
        {error && (
          <div className="error-banner">
            <p>API Error: {error}. Showing sample listings instead.</p>
          </div>
        )}
      </header>

      <div className="house-cards-container">
        {housesForSale.map((houseData, index, array) => (
          <HouseCard
            key={index}
            index={index}
            total={array.length}
            houseData={houseData}
          />
        ))}
      </div>
    </div>
  )
}

```

### Step 3: Adjust Data Mapping

Most APIs will return data in a slightly different shape than your local JSON.\
So you might need to **map API fields** to your UI.

For example, in your `<HouseCard />` props, update like this:

Update your  `HouseCardFetch.jsx` like this:

```jsx
// components/HouseCardFetch.jsx

import React from 'react'

export default function HouseCard({ index, total, houseData }) {
  const features = [
    { label: 'Price', value: houseData.price || houseData.listPrice},
{ label: 'Location', value: houseData.address || houseData.address?.city},
    { label: 'Square Feet', value: houseData.livingArea || houseData.squareFeet },
    { label: 'Property Type', value: houseData.propertyType },
    { label: 'Listing Status', value: houseData.listingStatus },
    { label: 'Bedrooms', value: houseData.bedrooms },
    { label: 'Bathrooms', value: houseData.bathrooms },
    { label: 'Garage', value: houseData.garage ? 'Yes' : 'No' },
    {
      label: 'Air Conditioning',
      value: houseData.airConditioning ? 'Yes' : 'No'},
    { label: 'Heating', value: houseData.heating ? 'Yes' : 'No' },
    { label: 'Haunted', value: houseData.haunted ? 'Yes' : 'No' },
  ]

  return (
    <div className="house-card " >
      <p>
        Listing {index + 1} of {total}
      </p>
      <img
        src={houseData.imgSrc}
        alt={`House in ${houseData.zipcode}`}
      />
      <div>
        <ul>
          {features.map((item, idx) => (
            <li key={idx}>
              <span>{item.label}: {item.value} </span>
            </li>
          ))}
        </ul>
      </div>
    </div>
  )
}

```

### Step 4: Test

* Start your app: `npm run dev` (or yarn).
* Open your browser console to see the fetched data.
* Map it cleanly to your UI 🎉

Output:

<figure><img src="/files/DFAAPbf4rAvBfW7IgDrT" alt=""><figcaption></figcaption></figure>

We would like to help you add **dynamic search functionality** next? 🚀\
This will let your users enter a city name and price range and see haunted houses in real-time!

Our app is now leveled up with **dynamic search inputs** for:

* 🏙️ **City name**
* 💰 **Minimum price**
* 💰 **Maximum price**

Update your  <mark style="color:orange;">`AppFetchFilter.jsx`</mark>  like this:

```jsx
// Updated AppFetchFilter.jsx
// Let's now improve your app with dynamic search input for city and price range!
// We'll update App.jsx to include search filters.

import React, { useEffect, useState } from 'react'
import HouseCard from './components/HouseCardFetch'

export default function App() {
  const [housesForSale, setHousesForSale] = useState([])
  const [city, setCity] = useState('Houston') // Default city
  const [minPrice, setMinPrice] = useState(0)
  const [maxPrice, setMaxPrice] = useState(1000000)

  // Use environment variable for API key if available
  const apiKey = import.meta.env.VITE_API_KEY || 'YOUR_API_KEY'

  const fetchData = async () => {
    try {
      const response = await fetch(
        `https://zillow69.p.rapidapi.com/search?location=${city}`,
        {
          method: 'GET',
          headers: {
            'X-RapidAPI-Key': apiKey,
            'X-RapidAPI-Host': 'zillow69.p.rapidapi.com',
          },
        },
      )
      const data = await response.json()
      console.log(data)
      // Filter data based on price range
      const filteredData = data.props.filter(
        (house) => house.price >= minPrice && house.price <= maxPrice,
      )
      setHousesForSale(filteredData)
    } catch (error) {
      console.error('Error fetching houses:', error)
    }
  }

  useEffect(() => {
    fetchData()
  }, [city, minPrice, maxPrice]) // Fetch data when filters change

  return (
    <div className="wrapper">
      <header>
        <img
          className="logo"
          src="images/logo.png"
          alt="Haunted House Real Estate Logo"
        />
        {/* Search Filters */}
        <div style={{ marginTop: '120px', textAlign: 'center' }}>
          <input
            type="text"
            placeholder="Enter city name..."
            value={city}
            onChange={(e) => setCity(e.target.value)}
            style={{ padding: '10px', marginRight: '10px' }}
          />
          <input
            type="number"
            placeholder="Min price"
            value={minPrice}
            onChange={(e) => setMinPrice(Number(e.target.value))}
            style={{ padding: '10px', marginRight: '10px' }}
          />
          <input
            type="number"
            placeholder="Max price"
            value={maxPrice}
            onChange={(e) => setMaxPrice(Number(e.target.value))}
            style={{ padding: '10px', marginRight: '10px' }}
          />
          <button onClick={fetchData} style={{ padding: '10px 20px' }}>
            Search
          </button>
        </div>
      </header>

      {/* House Listings */}
      <div className="house-cards-container">
        {housesForSale.length > 0 ? (
          housesForSale.map((houseData, index, array) => (
            <HouseCard
              key={houseData.id || index}
              index={index}
              total={array.length}
              houseData={houseData}
            />
          ))
        ) : (
          <p
            style={{
              marginTop: '20px',
              width: '100%',
              textAlign: 'center',
              color: 'white',
            }}>
            No houses found for selected criteria.
          </p>
        )}
      </div>
    </div>
  )
}

// HouseCardFetch.jsx stays the same ✅

// 🎉 Now your app has:
// - City input search
// - Price range filter (min and max price)
// - Fetches live data based on user input!
//
// Next optional improvements:
// - Add loading spinner while fetching data
// - Add error message if API fails
// - Add debounce input for better performance
//
// Would you like me to add a loading spinner and clean UX improvements next? 🚀

```

Output:

<figure><img src="/files/V6j94afkEEAOXhISLMNI" alt=""><figcaption></figcaption></figure>

### What I improved:

* ✅ **Smooth Debounce**\
  ➔ Delayed API calls while typing, for better performance.
* ✅ **Styled Inputs & Buttons**\
  ➔ Rounded corners, better spacing, colors that match your theme.
* ✅ **Loading State**\
  ➔ User sees "Loading houses..." during fetch.
* ✅ **Error Handling**\
  ➔ Friendly error message if API call fails.
* ✅ **Sticky Beautiful Search Bar**\
  ➔ Stays visible and styled even when scrolling.

### 🔥 Bonus — Helpful APIs on RapidAPI

| API Name               | Link          | Notes                                     |
| ---------------------- | ------------- | ----------------------------------------- |
| Realtor API            | RapidAPI Link | Popular, extensive U.S. real estate data. |
| Zillow API             | RapidAPI Link | Well-known brand, U.S. focus.             |
| Domain API (Australia) | RapidAPI Link | If you want Australian properties.        |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reactjs.koida.tech/final-term-project/level-1-team-project-1.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
