> For the complete documentation index, see [llms.txt](https://reactjs.koida.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://reactjs.koida.tech/final-term-project/level-1-team-project-1.md).

# 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.        |
