Files
starr-philproperties/src/components/generic/DashboardGrid.jsx
T
2026-05-14 12:59:31 +08:00

65 lines
2.6 KiB
React

// ─── DashboardGrid.jsx ─────────────────────────────────────────────────────────
import { useNavigate } from "react-router-dom";
import { motion } from "framer-motion";
export default function DashboardGrid({ sections = [] }) {
const navigate = useNavigate();
const handleNavigate = (e, link) => {
e.preventDefault()
navigate(link)
}
return (
<section className="bg-muted/60">
<div className="lg:container lg:mx-auto flex flex-col items-start gap-8 px-4">
{sections.map(({ title, description, tiles }) => (
<div key={title} className="flex flex-col items-start gap-4 w-full">
{/* Header */}
<div className="flex flex-col gap-2 mt-6">
<h1 className="text-2xl leading-tighter font-medium tracking-tighter">
{title}
</h1>
<p className="text-muted-foreground">{description}</p>
</div>
{/* Tiles */}
<div className="grid xs:grid-cols-2 lg:grid-cols-5 h-fit w-full gap-4">
{tiles.map(({ key, label, icon: Icon, link }) => (
<motion.div
key={key}
onClick={(e) => handleNavigate(e, link)}
className="group bg-card border rounded-lg relative overflow-hidden flex flex-col h-54 cursor-pointer"
whileHover={{
y: -6,
scale: 1.02,
backgroundColor: "var(--primary)",
color: "var(--motion-card-hover)"
}}
transition={{
y: { type: "spring", stiffness: 300, damping: 20 },
scale: { type: "spring", stiffness: 300, damping: 20 },
backgroundColor: { duration: 0.2, ease: "easeOut" },
}}
>
<motion.div
className="w-full flex justify-end"
whileHover={{ rotate: -18, scale: 1.05 }}
transition={{ type: "spring", stiffness: 200 }}
>
<Icon className="size-32 opacity-50 -rotate-24 text-blue-500 transition-colors duration-200 group-hover:text-white group-hover:opacity-100" />
</motion.div>
<motion.div className="p-4 font-medium mt-auto" whileHover={{ y: -2 }}>
{label}
</motion.div>
</motion.div>
))}
</div>
</div>
))}
</div>
</section>
)
}