"use client" import { useEffect, useMemo, useRef, useState } from "react" import Link from "next/link" import { motion, AnimatePresence, useMotionValue, useTransform } from "framer-motion" import { useFeatureFlag } from "@/components/feature-flags-provider" import { Button } from "@/components/ui/button" import { artists as staticArtists } from "@/data/artists" import { useActiveArtists } from "@/hooks/use-artists" import type { PublicArtist } from "@/types/database" export function ArtistsSection() { // Fetch artists from database const { data: dbArtistsData, isLoading, error } = useActiveArtists() // Merge static and database data const artists = useMemo(() => { // If still loading or error, use static data if (isLoading || error || !dbArtistsData) { return staticArtists } // Merge: use database portfolio images, keep static metadata return staticArtists.map(staticArtist => { const dbArtist = dbArtistsData.artists.find( (db) => db.slug === staticArtist.slug || db.name === staticArtist.name ) // If found in database, use its portfolio images if (dbArtist && dbArtist.portfolioImages.length > 0) { return { ...staticArtist, workImages: dbArtist.portfolioImages.map(img => img.url) } } // Fall back to static data return staticArtist }) }, [dbArtistsData, isLoading, error]) // Minimal animation: fade-in only (no parallax) const [visibleCards, setVisibleCards] = useState([]) const [hoveredCard, setHoveredCard] = useState(null) const [portfolioIndices, setPortfolioIndices] = useState>({}) const sectionRef = useRef(null) const advancedNavAnimations = useFeatureFlag("ADVANCED_NAV_SCROLL_ANIMATIONS_ENABLED") const allArtistIndices = useMemo(() => Array.from({ length: artists.length }, (_, idx) => idx), [artists.length]) useEffect(() => { if (!advancedNavAnimations) { setVisibleCards(allArtistIndices) return } setVisibleCards([]) }, [advancedNavAnimations, allArtistIndices]) useEffect(() => { if (!advancedNavAnimations) return const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const cardIndex = Number.parseInt(entry.target.getAttribute("data-index") || "0") setVisibleCards((prev) => [...new Set([...prev, cardIndex])]) } }) }, { threshold: 0.2, rootMargin: "0px 0px -10% 0px" }, ) const cards = sectionRef.current?.querySelectorAll("[data-index]") cards?.forEach((card) => observer.observe(card)) return () => observer.disconnect() }, [advancedNavAnimations]) const cardVisibilityClass = (index: number) => { if (!advancedNavAnimations) return "opacity-100 translate-y-0" return visibleCards.includes(index) ? "opacity-100 translate-y-0" : "opacity-0 translate-y-6" } const cardTransitionDelay = (index: number) => { if (!advancedNavAnimations) return undefined return `${index * 40}ms` } // Vary aspect ratio to create a subtle masonry rhythm const aspectFor = (i: number) => { const variants = ["aspect-[3/4]", "aspect-[4/5]", "aspect-square"] return variants[i % variants.length] } // Handle hover to cycle through portfolio images const handleHoverStart = (artistIndex: number) => { setHoveredCard(artistIndex) const artist = artists[artistIndex] if (artist.workImages.length > 0) { setPortfolioIndices((prev) => { const currentIndex = prev[artistIndex] ?? 0 const nextIndex = (currentIndex + 1) % artist.workImages.length return { ...prev, [artistIndex]: nextIndex } }) } } const handleHoverEnd = () => { setHoveredCard(null) } const getPortfolioImage = (artistIndex: number) => { const artist = artists[artistIndex] if (artist.workImages.length === 0) return null const imageIndex = portfolioIndices[artistIndex] ?? 0 return artist.workImages[imageIndex] } return (
{/* Faint logo texture */}
{/* Header */}

ARTISTS

Our exceptional team of tattoo artists, each bringing unique expertise and artistic vision to create your perfect tattoo.

{/* Masonry grid */}
{/* columns-based masonry; tighter spacing and wider section */}
{artists.map((artist, i) => { const transitionDelay = cardTransitionDelay(i) const portfolioImage = getPortfolioImage(i) const isHovered = hoveredCard === i return (
handleHoverStart(i)} onHoverEnd={handleHoverEnd} > {/* Base layer: artist portrait */}
{`${artist.name}
{/* Wipe overlay: portfolio image with curved boundary */} {isHovered && portfolioImage && ( <> {/* SVG clipPath with pronounced wave */} {/* Portfolio image with curved clip */}
{`${artist.name}
)}
{/* Minimal footer - only name */}

{artist.name}

{artist.specialty}

) })}
{/* CTA Footer */}

READY?

Choose your artist and start your tattoo journey with United Tattoo.

) }