This commit is contained in:
Jeremy Rangel
2025-11-30 00:50:56 -08:00
commit f1e7005b26
12 changed files with 844 additions and 0 deletions

139
DirectoryListingItem.js Normal file
View File

@ -0,0 +1,139 @@
import { useState, useEffect } from '@wordpress/element';
function isOpenNow(operatingHours = []) {
if (!operatingHours.length) return null;
const now = new Date();
const dayNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
const today = dayNames[now.getDay()];
const todaysHours = operatingHours.filter(h => h.day === today);
if (!todaysHours.length) return null;
for (let h of todaysHours) {
if (h.closed) continue;
if (h.open_24_hours) return true;
if (!h.opening_time || !h.closing_time) continue;
const [openH, openM] = h.opening_time.split(':').map(Number);
const [closeH, closeM] = h.closing_time.split(':').map(Number);
const openDate = new Date(now);
openDate.setHours(openH, openM, 0, 0);
const closeDate = new Date(now);
closeDate.setHours(closeH, closeM, 0, 0);
if (now >= openDate && now <= closeDate) return true;
}
return false;
}
export default function DirectoryListingItem({
post,
imageSrc = '',
imageMediaID = null,
restRoot = '', // must pass multisite REST root from View.js
onQuickView,
style = 'hero',
displayCategoryTitle = '',
displayCategoryURL = '#',
operatingHours = [],
titleTag,
postDescription,
}) {
const [finalImageSrc, setFinalImageSrc] = useState(imageSrc);
const [isOpen, setIsOpen] = useState(null);
const TitleTag = titleTag || 'h3';
useEffect(() => {
setIsOpen(isOpenNow(operatingHours));
}, [operatingHours]);
// Fetch image URL if imageMediaID is provided
useEffect(() => {
if (!imageMediaID) return;
if (!restRoot) {
console.error('DirectoryListingItem: restRoot is required for multisite media fetch.');
return;
}
let isMounted = true;
const url = `${restRoot}wp/v2/media/${imageMediaID}`;
console.log('Fetching media URL:', url);
fetch(url)
.then(res => {
if (!res.ok) throw new Error(`Media fetch failed: ${res.status}`);
return res.json();
})
.then(data => {
if (isMounted && data?.source_url) {
setFinalImageSrc(data.source_url);
}
})
.catch(err => console.error('Failed to fetch media:', err));
return () => { isMounted = false; };
}, [imageMediaID, restRoot]);
const heroStyle = style === 'hero' && finalImageSrc ? { backgroundImage: `url(${finalImageSrc})` } : {};
const content = (
<>
{style !== 'hero' && (
<div className="listing-left">
{finalImageSrc ? (
<img src={finalImageSrc} alt={post.title?.rendered || 'Post image'} />
) : (
<div className="placeholder-image">No Image</div>
)}
</div>
)}
<div className="listing-right">
{displayCategoryTitle && (
<a
className="listing-category-button"
href={displayCategoryURL}
target="_blank"
rel="noopener noreferrer"
>
{displayCategoryTitle}
</a>
)}
<TitleTag>
{post.title?.rendered ? (
<a href={post.link} dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
) : (
<span>No title</span>
)}
</TitleTag>
{postDescription ? (
<p dangerouslySetInnerHTML={{ __html: postDescription }} />
) : null}
{isOpen !== null && (
<span className={`open-status ${isOpen ? 'open' : 'closed'}`}>
{isOpen ? 'Open' : 'Closed'}
</span>
)}
<button className="quickview-button" onClick={() => onQuickView(post)}>
QuickView
</button>
</div>
</>
);
return style === 'hero' ? (
<a href={post.link} className={`directory-listing ${style}`} style={heroStyle}>
{content}
</a>
) : (
<article className={`directory-listing ${style}`} style={heroStyle}>
{content}
</article>
);
}