x
This commit is contained in:
1 parent
406cad801b
commit
a2da091b15
21 files changed
+2454
-63
No files matched your search
@@ -0,0 +1,140 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { FeaturedTilesSectionPayload } from '@homaroma/types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getLocalizedField } from '../utils/localization';
|
||||
|
||||
interface DynamicFeaturedTilesProps {
|
||||
payload: FeaturedTilesSectionPayload;
|
||||
}
|
||||
|
||||
export default function DynamicFeaturedTiles({ payload }: DynamicFeaturedTilesProps) {
|
||||
const { i18n } = useTranslation();
|
||||
const lang = i18n.language;
|
||||
const tiles = Array.isArray(payload.tiles) ? payload.tiles : [];
|
||||
const columns = payload.columns || 2;
|
||||
|
||||
const sectionTitle = getLocalizedField(payload.title, lang);
|
||||
const sectionSubtitle = getLocalizedField(payload.subtitle, lang);
|
||||
|
||||
const getGridColsClass = (cols: number) => {
|
||||
switch (cols) {
|
||||
case 3:
|
||||
return 'grid-cols-1 md:grid-cols-3';
|
||||
case 4:
|
||||
return 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4';
|
||||
case 2:
|
||||
default:
|
||||
return 'grid-cols-1 md:grid-cols-2';
|
||||
}
|
||||
};
|
||||
|
||||
const getAspectClass = (ratio?: string) => {
|
||||
switch (ratio) {
|
||||
case 'portrait':
|
||||
return 'aspect-[3/4]';
|
||||
case 'tall':
|
||||
return 'aspect-[2/3]';
|
||||
case 'auto':
|
||||
return 'aspect-auto';
|
||||
case 'square':
|
||||
default:
|
||||
return 'aspect-square';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
className="ui-section-band w-full overflow-hidden"
|
||||
style={{ backgroundColor: payload.backgroundColor, color: payload.textColor }}
|
||||
>
|
||||
<div className="ui-section-inner space-y-12">
|
||||
{/* Optional Section Header */}
|
||||
{(sectionTitle || sectionSubtitle) && (
|
||||
<div className="text-center space-y-3 max-w-2xl mx-auto">
|
||||
{sectionTitle && (
|
||||
<h2 className="font-serif text-3xl md:text-4xl font-light tracking-wide" style={{ color: payload.textColor }}>
|
||||
{sectionTitle}
|
||||
</h2>
|
||||
)}
|
||||
{sectionSubtitle && (
|
||||
<p className="text-sm md:text-base opacity-80 font-light" style={{ color: payload.textColor }}>
|
||||
{sectionSubtitle}
|
||||
</p>
|
||||
)}
|
||||
<div className="w-12 h-[1px] bg-pop-pink mx-auto"></div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Campaign Tiles Grid */}
|
||||
<div className={`grid gap-8 md:gap-12 ${getGridColsClass(columns)}`}>
|
||||
{tiles.map((tile, idx) => {
|
||||
const tileTitle = getLocalizedField(tile.title, lang);
|
||||
const tileDescription = getLocalizedField(tile.description, lang);
|
||||
const buttonText = getLocalizedField(tile.buttonText, lang);
|
||||
|
||||
const cardContent = (
|
||||
<>
|
||||
{/* Image Container */}
|
||||
<div className={`relative ${getAspectClass(tile.imageAspectRatio)} w-full overflow-hidden rounded-2xl bg-neutral-100 shadow-md`}>
|
||||
{tile.imageUrl ? (
|
||||
<img
|
||||
src={tile.imageUrl}
|
||||
alt={tileTitle}
|
||||
className="w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full bg-neutral-200 flex items-center justify-center text-neutral-400 text-xs">
|
||||
No Image
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Content Details */}
|
||||
<div className="space-y-2 max-w-md px-2 flex-1 flex flex-col justify-between w-full">
|
||||
<div className="space-y-2">
|
||||
{tileTitle && (
|
||||
<h3 className="font-serif text-xl md:text-2xl font-light tracking-wide text-neutral-800 group-hover:text-pop-pink transition-colors" style={{ color: payload.textColor }}>
|
||||
{tileTitle}
|
||||
</h3>
|
||||
)}
|
||||
{tileDescription && (
|
||||
<p className="text-xs md:text-sm font-light leading-relaxed opacity-80" style={{ color: payload.textColor }}>
|
||||
{tileDescription}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* CTA Button */}
|
||||
{buttonText && tile.buttonLink && (
|
||||
<div className="pt-3">
|
||||
<span className="inline-block px-7 py-3 text-xs font-medium tracking-wider uppercase transition-all duration-300 bg-neutral-900 text-white group-hover:bg-neutral-800 rounded-sm shadow-sm">
|
||||
{buttonText}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div key={idx} className="flex flex-col items-center text-center group w-full">
|
||||
{tile.buttonLink ? (
|
||||
<Link to={tile.buttonLink} className="w-full flex flex-col items-center text-center space-y-5">
|
||||
{cardContent}
|
||||
</Link>
|
||||
) : (
|
||||
<div className="w-full flex flex-col items-center text-center space-y-5">
|
||||
{cardContent}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user