1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { a as api } from "./index-D0Bmh-UX.js"; 3import { g as getColorsFromHash, m as getOptimizedImageUrl } from "./utils-BYCVoSTN.js"; 4import { g as getBilingualTitle, p as parseMultilingualData } from "./bilingual-B8ftIVs5.js"; 5import { u as useTranslation } from "./vendor-i18n-R-bMoYp_.js"; 6import { cj as Layers, cL as RefreshCw, bi as Sparkles, bN as Library, dM as Calendar, cm as ArrowRight } from "./vendor-dVNaLJx_.js"; 7const CollectionIndexPage = ({ onNavigate }) => { 8 const { t, i18n } = useTranslation(); 9 const [collections, setCollections] = reactExports.useState([]); 10 const [isLoading, setIsLoading] = reactExports.useState(true); 11 const [error, setError] = reactExports.useState(null); 12 const [imageErrors, setImageErrors] = reactExports.useState({}); 13 reactExports.useEffect(() => { 14 document.title = t("collections.pageTitle", "Browse Curated MIDI Collections | HamieNET"); 15 const metaDesc = document.querySelector('meta[name="description"]'); 16 if (metaDesc) { 17 metaDesc.setAttribute("content", "Explore hand-crafted collections of MIDI files organized by theme, composer, style, and musical eras. Discover lists like Broadway Musicals, Classical Masters, and more."); 18 } 19 const fetchCollections = async () => { 20 try { 21 setIsLoading(true); 22 const data = await api.getCollections(); 23 setCollections(data); 24 setError(null); 25 } catch (err) { 26 console.error("[CollectionIndexPage Load Error]", err); 27 const errMsg = err instanceof Error ? err.message : "Failed to load collections. Please try again later."; 28 setError(errMsg); 29 } finally { 30 setIsLoading(false); 31 } 32 }; 33 fetchCollections(); 34 }, [t]); 35 if (isLoading) { 36 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center min-h-[60vh] bg-zinc-50 dark:bg-[#0a0a0a]", children: [
37 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mb-4" }), 38 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm font-semibold animate-pulse", children: t("collections.loading", "Loading themed collections...") }) 39 ] }); 40 } 41 if (error) { 42 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-md mx-auto my-20 p-8 rounded-2xl bg-red-50 dark:bg-red-950/20 border border-red-200 dark:border-red-900/30 text-center animate-in fade-in", children: [ 43 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { className: "mx-auto text-red-500 dark:text-red-400 mb-4", size: 40 }), 44 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-red-800 dark:text-red-300 mb-2", children: t("collections.errorTitle", "Error Loading Collections") }), 45 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-red-600 dark:text-red-400 mb-6", children: error }),
46 /* @__PURE__ */ jsxRuntimeExports.jsxs( 47 "button", 48 { 49 onClick: () => window.location.reload(), 50 className: "inline-flex items-center gap-2 bg-red-600 hover:bg-red-700 text-white font-bold text-sm px-6 py-2.5 rounded-xl transition shadow-lg shadow-red-600/20", 51 children: [ 52 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14 }), 53 t("collections.retry", "Retry") 54 ] 55 } 56 ) 57 ] }); 58 } 59 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 md:p-10 max-w-[2000px] mx-auto animate-in fade-in duration-700 bg-zinc-50 dark:bg-[#0a0a0a] min-h-full", children: [ 60 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-4xl mb-12", children: [ 61 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex items-center gap-2 text-indigo-500 font-black text-xs uppercase tracking-widest mb-3 bg-indigo-50 dark:bg-indigo-950/30 px-3 py-1 rounded-full border border-indigo-100 dark:border-indigo-900/40", children: [
62 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14 }), 63 t("collections.curatedSoundscapes", "Curated Soundscapes") 64 ] }), 65 /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-4xl md:text-5xl font-black tracking-tight text-zinc-900 dark:text-white mb-4", children: t("collections.browseCollections", "Browse Collections") }), 66 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-600 dark:text-zinc-400 text-base md:text-lg font-medium leading-relaxed", children: t("collections.browseDescription", "Discover hand-crafted selections of MIDI tracks grouped by themes, stage shows, composers, and styles. Each collection is carefully arranged to guide you through different musical journeys.") }) 67 ] }), 68 collections.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-12 text-center rounded-3xl border-2 border-dashed border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900/30 max-w-xl mx-auto my-12", children: [ 69 /* @__PURE__ */ jsxRuntimeExports.jsx(Library, { className: "mx-auto text-zinc-400 dark:text-zinc-600 mb-4", size: 48 }),
70 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-zinc-700 dark:text-zinc-300 mb-1", children: t("collections.noCollections", "No Collections Published") }), 71 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: t("collections.noCollectionsDesc", "Check back later! We are working on curating some amazing MIDI playlists and thematic pages for you.") }) 72 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8", children: collections.map((collection, idx) => { 73 const colors = getColorsFromHash(collection.title + collection.slug); 74 const formattedDate = collection.createdAt ? new Date(collection.createdAt).toLocaleDateString(void 0, { 75 year: "numeric", 76 month: "short" 77 }) : null; 78 const bilingual = getBilingualTitle(collection.title, collection.subtitle, i18n.language); 79 const descDict = parseMultilingualData(collection.description); 80 const lang = (i18n.language || "en").toLowerCase(); 81 const langPrefix = lang.split("-")[0]; 82 const localizedDescription = descDict && (descDict[lang] || descDict[langPrefix] || descDict.en || Object.values(descDict)[0]) || (typeof collection.description === "string" && !collection.description.trim().startsWith("{") && !collection.description.trim().startsWith("[") ? collection.description : null); 83 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 84 "a", 85 { 86 href: `/collections/${collection.slug}`, 87 onClick: (e) => { 88 if (!e.ctrlKey && !e.metaKey && !e.shiftKey && e.button === 0) { 89 e.preventDefault(); 90 onNavigate("collection", collection.slug); 91 } 92 }, 93 className: "group relative rounded-3xl overflow-hidden bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800/80 shadow-md hover:shadow-2xl hover:shadow-indigo-500/10 transition-all duration-500 hover:-translate-y-2 cursor-pointer flex flex-col h-[400px] animate-in fade-in slide-in-from-bottom-8 no-underline text-inherit", 94 style: { animationDelay: `${idx * 80}ms` }, 95 title: bilingual.hoverTooltip, 96 children: [
97 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-48 w-full overflow-hidden shrink-0 select-none", children: [ 98 collection.heroImage && !imageErrors[collection.id] ? /* @__PURE__ */ jsxRuntimeExports.jsx( 99 "img", 100 { 101 src: getOptimizedImageUrl(collection.heroImage, "card"), 102 alt: bilingual.altText, 103 title: bilingual.hoverTooltip, 104 className: "w-full h-full object-cover transition-transform duration-700 group-hover:scale-110", 105 loading: "lazy", 106 onError: () => setImageErrors((prev) => ({ ...prev, [collection.id]: true })) 107 } 108 ) : /* @__PURE__ */ jsxRuntimeExports.jsx( 109 "div", 110 { 111 className: "w-full h-full transition-transform duration-700 group-hover:scale-110", 112 style: { background: colors.gradient }, 113 title: bilingual.hoverTooltip 114 } 115 ),
116 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/80 via-black/30 to-transparent" }), 117 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-4 right-4 flex items-center gap-1.5", children: [ 118 bilingual.nativeBadge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-emerald-600/90 text-white font-black text-[10px] tracking-wider px-2 py-1 rounded-lg backdrop-blur-sm shadow border border-emerald-400/30", children: bilingual.nativeBadge }), 119 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-indigo-600/90 text-white font-black text-[10px] uppercase tracking-wider px-3 py-1 rounded-xl backdrop-blur-sm shadow-lg border border-indigo-400/20", children: collection.sectionCount === 1 ? t("collections.sectionCount_one", "1 Section", { count: 1 }) : t("collections.sectionCount_other", "{{count}} Sections", { count: collection.sectionCount || 0 }) }) 120 ] }) 121 ] }),
122 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 flex-1 flex flex-col justify-between", children: [ 123 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [ 124 /* @__PURE__ */ jsxRuntimeExports.jsx( 125 "h2", 126 { 127 className: "text-xl font-extrabold text-zinc-900 dark:text-white line-clamp-2 group-hover:text-indigo-500 transition-colors leading-tight", 128 title: bilingual.hoverTooltip, 129 children: bilingual.mainTitle 130 } 131 ), 132 bilingual.subTitle && /* @__PURE__ */ jsxRuntimeExports.jsx( 133 "p", 134 { 135 className: "text-zinc-500 dark:text-zinc-400 text-xs font-semibold uppercase tracking-wider line-clamp-1", 136 title: bilingual.hoverTooltip, 137 children: bilingual.subTitle 138 } 139 ), 140 localizedDescription && /* @__P
140URE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm line-clamp-2 leading-relaxed pt-1", children: localizedDescription }) 141 ] }), 142 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pt-4 border-t border-zinc-100 dark:border-zinc-800/80 mt-4 shrink-0", children: [ 143 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1 text-[11px] text-zinc-400 dark:text-zinc-500 font-bold uppercase", children: formattedDate && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 144 /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { size: 12 }), 145 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formattedDate }) 146 ] }) }), 147 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-[11px] font-black uppercase tracking-widest text-indigo-500 group-hover:text-indigo-400 transition-colors", children: [ 148 t("collections.explore", "Explore"),
149 /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { size: 12, className: "transform transition-transform duration-300 group-hover:translate-x-1" }) 150 ] }) 151 ] }) 152 ] }) 153 ] 154 }, 155 collection.id 156 ); 157 }) }) 158 ] }); 159}; 160export { 161 CollectionIndexPage as default 162};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.