Property Card Dynamic Data

Properties Pages and Property Card Dynamic Data

1. Prepare Your Data Source

a. Place the JSON File

  • Ensure that your properties.json file is located in your project root (or in a dedicated data folder).

  • This file contains an array of property objects with fields such as _id, name, type, location, rates, images, etc.

b. Understand the JSON Structure

  • Each property has:

    • Basic Details: _id, name, type, beds, baths, square_feet

    • Location: A nested object with street, city, state, zipcode

    • Rates: An object that might include nightly, weekly, or monthly values

    • Images: An array of image filenames (to be prefixed with /images/properties/ for public access)


2. Create a Dynamic PropertyCard Component

a. Create the Component File

  • In your components/ folder, create a file named PropertyCard.jsx.

b. Import Required Modules

  • Import Next.js’ Image and Link components along with React icons for a more stylish look:

    "use client"; // Enable client-side interactivity if needed
    import Image from 'next/image';
    import Link from 'next/link';
    import { FaBed, FaBath, FaRulerCombined, FaMoneyBill, FaMapMarkerAlt } from 'react-icons/fa';

c. Build the Component Structure

  • The component receives a single property object as a prop and dynamically displays its data. Create a helper function to determine the proper rate display.

  • Example Code:

    'use client' // Enable client-side interactivity if needed
    import Image from 'next/image'
    import Link from 'next/link'
    import {
      FaBed,
      FaBath,
      FaRulerCombined,
      FaMoneyBill,
      FaMapMarkerAlt,
    } from 'react-icons/fa'
    
    export default function PropertyCard({ property }) {
      // Helper function to choose a rate to display
      const getRateDisplay = () => {
        const { rates } = property
        if (rates.monthly) {
          return `$${rates.monthly.toLocaleString()}/mo`
        } else if (rates.weekly) {
          return `$${rates.weekly.toLocaleString()}/wk`
        } else if (rates.nightly) {
          return `$${rates.nightly.toLocaleString()}/night`
        }
        return 'Contact for rates'
      }
    
      return (
        <div className="bg-white rounded-xl shadow-md relative ">
          {/* Display the first image from the array */}
          <Image
            src={`/images/properties/${property.images[0]}`}
            alt={property.name}
            width={0} // Set to 0 when using CSS for responsive sizing
            height={0}
            sizes="100vw"
            className="object-cover rounded-t-xl md:rounded-tr-none md:rounded-l-xl w-full "
          />
    
          <div className="p-6">
            <div className="text-left md:text-center lg:text-left mb-6">
              <h3 className="text-xl font-bold">{property.name}</h3>
              <div className="text-gray-600 mb-4">{property.type}</div>
            </div>
            {/* Rate display badge */}
            <h3 className="absolute top-2 left-2 bg-white px-4 py-2 rounded-lg text-blue-500 font-bold">
              {getRateDisplay()}
            </h3>
    
            <div className="flex justify-between items-center ">
              <div className="flex gap-2 text-gray-500">
                <p>
                  <FaBed className="inline mr-1" /> {property.beds}
                </p>
                <p>
                  <FaBath className="inline mr-1" /> {property.baths}
                </p>
                <p>
                  <FaRulerCombined className="inline mr-1" /> {property.square_feet}{' '}
                  sqft
                </p>
              </div>
            </div>
            <div className="flex justify-center gap-4 text-green-900 text-sm mb-4">
              {property.rates.nightly && (
                <p>
                  <i className="fa-solid fa-money-bill"></i> Nightly
                </p>
              )}
              {property.rates.weekly && (
                <p>
                  <i className="fa-solid fa-money-bill"></i> Weekly
                </p>
              )}
              {property.rates.monthly && (
                <p>
                  <i className="fa-solid fa-money-bill"></i> Monthly
                </p>
              )}
            </div>
          </div>
          <div className="border border-gray-200 mb-5">
            <div className="flex flex-col lg:flex-row justify-between mb-4">
              <div className=" flex align-middle gap-2 mb-4 lg:mb-0">
                <FaMapMarkerAlt className="text-orange-700 mr-1" />
                <span className="text-orange-700">
                  {property.location.city}, {property.location.state}
                </span>
              </div>
              <Link
                href={`/properties/${property._id}`}
                className="h-[36px]  bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg text-sm">
                Details
              </Link>
            </div>
          </div>
        </div>
      )
    }

Explanation:

  1. 'use client':

    • This directive at the top indicates that this component is a Client Component in Next.js. This means it will be rendered in the user's browser and can use client-side features like event handlers and state.

  2. Imports:

    • Image from next/image: For optimized image rendering.

    • Link from next/link: For creating client-side navigation links.

    • FaBed, FaBath, FaRulerCombined, FaMoneyBill, FaMapMarkerAlt from react-icons/fa: These are icons from the react-icons library (specifically, Font Awesome icons) used to visually represent beds, baths, square footage, rates, and location.

  3. PropertyCard({ property }) Function:

    • This is a functional component that accepts a property object as a prop. This property object will contain all the details of a single property (like name, type, location, rates, etc.).

  4. getRateDisplay() Helper Function:

    • This function determines which rate to display (monthly, weekly, or nightly) based on the rates object within the property data.

    • It prioritizes monthly, then weekly, then nightly.

    • It uses toLocaleString() to format the number with commas (e.g., 4200 becomes 4,200).

    • If no rates are available, it returns "Contact for rates".

  5. JSX Structure (The return statement):

    • Outer div:

      • className="bg-white rounded-xl shadow-md relative": This sets the basic styling for the card: white background, rounded corners, a shadow, and relative positioning (for absolute positioning of the rate badge).

    • Image Component:

      • src={/images/properties/${property.images[0]}}: Dynamically sets the image source to the first image in the property.images array. It assumes images are stored in the /public/images/properties directory.

      • alt={property.name}: Sets the alt text for accessibility.

      • width={0}, height={0}, sizes="100vw": These are important for responsive image sizing with next/image. By setting width and height to 0, you're telling Next.js to let the CSS determine the image dimensions. sizes="100vw" tells Next.js that the image will take up the full width of the viewport.

      • className="object-cover rounded-t-xl md:rounded-tr-none md:rounded-l-xl w-full": Styles the image to cover the container, have rounded corners at the top, and adjust the rounding on medium screens and up.

    • Inner div (Content):

      • className="p-6": Adds padding to the content area.

      • Property Name and Type:

        • h3 className="text-xl font-bold">{property.name}: Displays the property's name in a large, bold font.

        • div className="text-gray-600 mb-4">{property.type}: Displays the property's type (e.g., "Apartment", "Condo").

      • Rate Badge:

        • h3 className="absolute top-2 left-2 bg-white px-4 py-2 rounded-lg text-blue-500 font-bold": Displays the rate (from getRateDisplay()) as a badge in the top-left corner of the card.

      • Beds, Baths, and Square Footage:

        • div className="flex justify-between items-center": A flex container to align the icons and text.

        • div className="flex gap-2 text-gray-500": A flex container for the icons and text.

        • <FaBed>, <FaBath>, <FaRulerCombined>: The icons.

        • {property.beds}, {property.baths}, {property.square_feet}: The property data.

      • Rate Types:

        • div className="flex justify-center gap-4 text-green-900 text-sm mb-4": A flex container to display the rate types.

        • The code conditionally renders the rate types based on the property data.

      • Location and Details Link

        • div className="border border-gray-200 mb-5": A horizontal line to separate the content.

        • div className="flex flex-col lg:flex-row justify-between mb-4": A flex container to align the location and the details link.

        • <FaMapMarkerAlt>: The location icon.

        • {property.location.city}, {property.location.state}: The property's city and state.

        • <Link href={/properties/${property._id}}: A link to the property's detail page. The _id is used to create a unique URL for each property.

In Summary:

The PropertyCard component is a reusable UI element that takes property data and renders a visually appealing card with key information about the property. It handles:

  • Displaying an image.

  • Showing the property's name, type, and location.

  • Dynamically choosing and formatting the appropriate rate.

  • Displaying the number of beds, baths, and square footage.

  • Providing a link to the full property details page.

  • Responsive design.

  • Using icons to improve the UI.


3. Integrate the PropertyCard Component in the Properties Page

a. Create/Update the Properties Page

  • In your Next.js project (using the new app directory), create or update the file:

    app/properties/page.jsx

b. Import the JSON Data and Component

  • Import your JSON file and the PropertyCard component:

    import PropertyCard from '@/components/PropertyCard';
    import properties from '@/properties.json'; // Adjust path if necessary

c. Render the List of Property Cards

  • Update page.jsx by applying conditional rendering to check if properties exist. If so, map over the data and render a PropertyCard for each item.

  • Example Code:

    //app/properties/page.jsx
    import properties from '@/properties.json' // Adjust the path as needed
    import PropertyCard from '@/components/PropertyCard'
    
    export default function PropertiesPage() {
      return (
        <section className="container mx-auto px-4 py-6">
          <h2 className="text-3xl font-bold text-blue-500 mb-6 text-center">All Listings</h2>
          {properties.length === 0 ? (
            <p>No properties found.</p>
          ) : (
            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
              {properties.map((property) => (
                <PropertyCard key={property._id} property={property} />
              ))}
            </div>
          )}
        </section>
      );
    }

4. Testing and Refinements

a. Run the Development Server

  • Start your Next.js server:

    npm run dev
  • Open your browser and navigate to http://localhost:3000/properties.

b. Verify Dynamic Data Rendering

  • Ensure that each property card displays the dynamic data from properties.json:

    • The image should load from the public folder (using the first image in the array).

    • Property details like name, type, rates, beds, baths, and location should display correctly.

    • The “Details” link should dynamically point to the property’s unique route (e.g., /property/1).

c. Adjust Component Styling if Needed

  • Refine Tailwind CSS classes and layout to achieve the desired visual appearance and responsiveness.

  • Verify that the Next.js Image component is handling responsive image sizes appropriately.


5. Future Enhancements

  • API Integration: Replace the static JSON import with an API call or database query for real-time data.

  • Pagination & Filtering: Implement pagination and search/filter functionalities to manage large sets of property listings.

  • Dynamic Routing: Create a detailed property page that displays full information when a user clicks “Details.”


Recap

  1. Data Source Preparation: Place and understand the properties.json file.

  2. PropertyCard Component: Create a dynamic, reusable component that accepts a property object as a prop and displays its data using Next.js Image and React icons.

  3. Properties Page Integration: Import the JSON data and map through it to render a list of property cards.

  4. Testing: Run your development server and verify that the property cards are rendered dynamically with real-time data.

  5. Future Enhancements: Plan for API integration, pagination, and detailed property pages.

Last updated