I'm using MERN stack and building production for the website server but the meta is captured from my index.js only. I'm using REACT-HELMET for each and every page but the metadata,it does not capture. I have read a lot of articles that say SSR and CSR
I'm not sure MERN belongs to which one. cause I am using express for API is SSR or CSR? I'm a bit confused.
and how could I make my share link from a subpage e.g:www.test.com/product/x123123*id (mapping data from API server) e.g. product Catfood, title and image can be showing displayed on Social like Facebook metadata: og:image and the title will change accordingly.
i have tested react-snap but it does not work.
import React, { useEffect, useState } from 'react';
import {
addViewAction,
getlistingAction,
likeListingAction,
unLikeListingAction,
} from '../../redux/slices/listingSlices';
import { getUserProfileAction } from '../../redux/slices/userSlices';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import ErrorMsg from '../../components/ErrorMsg/ErrorMsg';
import LoadingComponent from '../../components/LoadingComponent/loadingComponent';
import ReactImageGallery from 'react-image-gallery';
import 'react-image-gallery/styles/css/image-gallery.css';
import BackButton from '../../components/Backbutton';
import {
FaBed,
FaRulerCombined,
FaBath,
FaRegHeart,
FaHeart,
FaFacebook,
FaWhatsapp,
FaEnvelope,
FaViber,
FaTwitter,
FaLine,
FaTelegram,
FaShareAlt,
FaPhone,
FaMap,
FaMailBulk,
FaEye,
} from 'react-icons/fa';
import { LuLampFloor } from 'react-icons/lu';
import SuccesMsg from '../../components/SuccessMsg/SuccessMsg';
import MortgageCalculator from '../../components/MorgageCalculator';
import {
FacebookShareButton,
EmailShareButton,
ViberShareButton,
TwitterShareButton,
LineShareButton,
TelegramShareButton,
WhatsappShareButton,
} from 'react-share';
import ReactPlayer from 'react-player';
import { Helmet } from 'react-helmet';
const ListingDetails = ({ color }) => {
const [myProfile, setMyProfile] = useState(null);
const [myListing, setMyListing] = useState(null);
const [isLiked, setIsLiked] = useState(false);
const [coordinates, setCoordinates] = useState([]);
const [showShareTooltip, setShowShareTooltip] = useState(false);
const dispatch = useDispatch();
const params = useParams();
const listingId = params.id;
// eslint-disable-next-line
const { listing, error, loading, success } = useSelector(
(state) => state?.listing
);
useEffect(() => {
const fetchData = async () => {
try {
await dispatch(addViewAction(listingId));
const listingResponse = await dispatch(getlistingAction(listingId));
const listing = listingResponse.payload.listing;
setMyListing(listing);
await setCoordinates([listing.coordinateX, listing.coordinateY]);
// Fetch user profile after fetching the listing
const profileResponse = await dispatch(getUserProfileAction());
setMyProfile(profileResponse.payload.myProfile);
// Fetch liked status after fetching user profile
const likedUsers = listing?.likes;
const hasLike = likedUsers?.some((userIds) =>
userIds.includes(myProfile?._id)
);
setIsLiked(hasLike);
} catch (error) {
console.error('Error fetching data:', error);
}
};
if (!myListing) {
fetchData();
}
}, [dispatch, listingId, myListing, myProfile]);
const refreshListing = async () => {
try {
const response = await dispatch(getlistingAction(listingId));
setMyListing(response.payload.listing);
} catch (error) {
console.error('Error refreshing listing:', error);
}
};
const likeListingHandler = async () => {
try {
await dispatch(likeListingAction(listingId));
refreshListing();
} catch (error) {
console.error('Error liking listing:', error);
}
};
const unListingHandler = async () => {
try {
await dispatch(unLikeListingAction(listingId));
refreshListing();
} catch (error) {
console.error('Error unliking listing:', error);
}
};
if (loading || !myListing) {
return <LoadingComponent />;
}
if (error) {
return <ErrorMsg message={error} />;
}
const homevalue = myListing ? myListing.price : 0; // Replace 0 with a default value if needed
const downpayment = (homevalue * 10) / 100; //
const images = myListing.images.map((image, index) => ({
original: image,
description: `Photo ${index + 1}`,
}));
// Assuming myListing.description contains the property description text
const description = myListing.description;
// Replace newline characters with <br> tags
const formattedDescription = description.replace(/\n/g, '<br>');
const openGoogleMapInNewWindow = (coordinates) => {
// Define the URL for the new window with coordinates as a query parameter
const url = `/google-map?lat=${coordinates[0]}&lng=${coordinates[1]}`;
const newWindow = window.open(url, '_blank', 'width=800,height=600');
// Handle any pop-up blocker issues
if (!newWindow) {
alert('Please allow pop-ups to open the map.');
}
};
//============================================================================================================================
//====================================================BELOW is RETURN ========================================================
//============================================================================================================================
return (
<div className="dark:bg-gray-900">
<Helmet>
<title>{myListing.title}</title>
<meta property="og:title" content={myListing.title} />
<meta property="og:image" content={myListing.images[0]} />
<meta property="og:type" content="website" />
</Helmet>
<div className="container mx-auto bg-gray-100 dark:bg-gray-900 h-full ">
<div className="flex justify-start py-4 pl-5">
<BackButton />
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 bg-gray-100 dark:bg-gray-900 p- h-full pt-5">
<div className="">
<div className="flex">
<ReactImageGallery items={images} />
</div>
{myListing.videoURL ? (
<div className="flex justify-center align-middle p-2 lg:py-16">
<ReactPlayer url={myListing.videoURL} />
</div>
) : null}
</div>
<div className="container p-2 mx-auto">
<div className="w-auto rounded-xl overflow-hidden shadow-lg dark:bg-gray-800 p-5 text-gray-600">
<div className="p-3">
<div className="font-bold text-xl mb-2 dark:text-gray-300">
For {myListing.propertyType}
</div>
<div className="font-bold text-xl mb-2 dark:text-gray-300">
{myListing.title}
</div>
<div className="mb-2 dark:text-gray-500">
{myListing.address}
</div>
<div className="grid grid-cols-3 gap-4 text-center">
{myProfile ? (
!isLiked ? (
<button
onClick={likeListingHandler}
className="flex items-center gap-1 m-2 text-2xl text-gray-400"
>
<FaRegHeart />
</button>
) : (
<button
onClick={unListingHandler}
className="flex items-center gap-1 m-2 text-2xl text-gray-400"
>
<FaHeart className="text-red-700" />
</button>
)
) : null}{' '}
<div className="p-2">
<button
onClick={() => openGoogleMapInNewWindow(coordinates)}
className="text-2xl text-gray-400 focus:outline-none"
>
<FaMap />
</button>
</div>
<div className="p-2">
<button
type="button"
className="text-2xl text-gray-400 focus:outline-none"
onClick={() => setShowShareTooltip(!showShareTooltip)}
>
<FaShareAlt />
</button>
{showShareTooltip && (
<div className="absolute z-10 mt-2 -ml-2 transform -translate-x-1/2 w-36 p-2 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5">
<div className="py-1 text-start">
<FacebookShareButton url={window.location.href}>
<div className="flex items-center">
<FaFacebook className="text-xl mr-2" />
Facebook
</div>
</FacebookShareButton>
<WhatsappShareButton url={window.location.href}>
<div className="flex items-center">
<FaWhatsapp className="text-xl mr-2" />
WhatsApp
</div>
</WhatsappShareButton>
<EmailShareButton url={window.location.href}>
<div className="flex items-center">
<FaEnvelope className="text-xl mr-2" />
Email
</div>
</EmailShareButton>
<ViberShareButton url={window.location.href}>
<div className="flex items-center">
<FaViber className="text-xl mr-2" />
Viber
</div>
</ViberShareButton>
<TwitterShareButton url={window.location.href}>
<div className="flex items-center">
<FaTwitter className="text-xl mr-2" />
Twitter
</div>
</TwitterShareButton>
<LineShareButton url={window.location.href}>
<div className="flex items-center">
<FaLine className="text-xl mr-2" />
Line
</div>
</LineShareButton>
<TelegramShareButton url={window.location.href}>
<div className="flex items-center">
<FaTelegram className="text-xl mr-2" />
Telegram
</div>
</TelegramShareButton>
{/* Add more social media share buttons here */}
</div>
</div>
)}
</div>
</div>
<div className="grid grid-cols-2 gap-1 px-1 py-3 text-gray-500 dark:text-gray-300">
<div className="flex items-center text-xl">
<FaBed className="mr-2" /> {myListing.beds} Room
</div>
<div className="flex items-center text-xl">
<FaRulerCombined className="mr-2" /> {myListing.sqfeet} Sqft
</div>
<div className="flex items-center text-xl">
<FaBath className="mr-2" /> {myListing.bathroom} Bath
</div>
<div className="flex items-center text-xl">
<LuLampFloor className="mr-2" /> {myListing.furnishing}
</div>
</div>
</div>
<hr />
<div className="p-2">
{!myListing.propertyType === 'buy' ? (
<div className="font-bold text-xl mb-2 dark:text-gray-300">
Price: {myListing.price} / month
</div>
) : (
<div className="font-bold text-xl mb-2 dark:text-gray-300">
RM {myListing.price.toLocaleString()}
{myListing.propertyType !== 'Rent' ? null : '/ Month'}
</div>
)}
</div>
<div className="p-2">
<div className="mb-2 dark:text-gray-300">
RM {myListing.psf} Per Sqft
</div>
<div className="mb-2 dark:text-gray-300">
{myListing.psf > 43560
? `${myListing.psf / 43560} Acres`
: `${myListing.psf} Sqft`}
</div>
</div>
<div className="dark:text-gray-300 p-5 col-span-2">
<hr />
<label className="font-bold">Unit Features</label>
<hr />
<div className="overflow-auto max-h-40">
{' '}
{/* Add a max-height and overflow property */}
{myListing.unitFeatures}
</div>
</div>
<div className="dark:text-gray-300 p-5 col-span-2">
<hr />
<label className="font-bold">Facilities</label>
<hr />
<div className="overflow-auto max-h-40">
{' '}
{/* Add a max-height and overflow property */}
{myListing.facilities}
</div>
</div>
<div className="font-bold text-xl mb-2 dark:text-gray-300 p-3">
About This Property <br />
<br />
<p
className="text-sm font-lite"
dangerouslySetInnerHTML={{ __html: formattedDescription }}
></p>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="text-lg font-bold flex items-center">
<FaEye className="mr-3" />
{myListing.numViews}
</div>
</div>
<div className="pt-4 pb-2 grid grid-cols-3 gap-2 text-center">
<a
href={`https://wa.me/${encodeURIComponent(
myListing.user.whatsapp
)}/?text=I%20am%20interested%20in%20${encodeURIComponent(
myListing.title
)}.%20Please%20provide%20me%20more%20information.`}
className="flex flex-col items-center text-gray-800 dark:text-gray-100 bg-gray-200 dark:bg-gray-900 p-3 py-2 rounded-xl text-center"
target="_blank"
rel="noopener noreferrer"
>
<FaWhatsapp className="text-2xl mb-1" />
<p className="text-sm">Whatsapp</p>
</a>
<button
onClick={() =>
(window.location = `mailto:${myListing.user.email}`)
}
className="flex flex-col items-center text-gray-800 dark:text-gray-100 bg-gray-200 dark:bg-gray-900 p-3 py-2 rounded-xl"
>
<FaMailBulk className="text-2xl mb-1" />
<p className="text-sm">Email</p>
</button>
<a
href={`tel:+${myListing.user.contactNumber}`}
className="flex flex-col items-center text-gray-800 dark:text-gray-100 bg-gray-200 dark:bg-gray-900 p-3 py-2 rounded-xl"
>
<FaPhone className="text-2xl mb-1" />
<p className="text-sm">Call</p>
</a>
</div>
</div>
{success && <SuccesMsg message="Successfully Load" />}
</div>
</div>
<div className="p-5 lg:p-10">
{myListing.propertyType === 'Sale' ? (
<MortgageCalculator
homevalue={homevalue}
downpayment={downpayment}
/>
) : null}
</div>
</div>
</div>
);
};
export default ListingDetails;