/* global React, PAPERS, BRANDS, FINISHES, COLOR_GROUPS, CATEGORIES, PaperCard, BrandStamp, Icon */
const { useState, useEffect, useMemo, useCallback, useRef } = React;
function GalleryPage({ navigate, tweaks, cart, addToCart, compare, toggleCompare, openCompareModal, query, catalog, catalogLoading, catalogError }) {
// ----- Filter state -----
const [activeCat, setActiveCat] = useState(query.cat || "all");
const [selBrands, setSelBrands] = useState(query.brand ? [query.brand] : []);
const [selFinishes, setSelFinishes] = useState([]);
const [selColors, setSelColors] = useState([]);
const [gsmMin, setGsmMin] = useState(60);
const [gsmMax, setGsmMax] = useState(350);
const [sort, setSort] = useState("featured");
const [drawerOpen, setDrawerOpen] = useState(false);
const [mobileAccOpen, setMobileAccOpen] = useState(false);
const PAGE_SIZE = 12;
const [page, setPage] = useState(1);
var activePapers = catalog && catalog.length > 0 ? catalog : (window.PAPERS || []);
var activeCategories = (catalog && catalog.length > 0 && catalog[0] && catalog[0].acc_id)
? (window.ACC_CATEGORIES || window.CATEGORIES)
: window.CATEGORIES;
// Reset activeCat kalau value lama tidak ada di activeCategories baru
useEffect(() => {
if (activeCat !== "all" && !activeCategories.find(c => c.id === activeCat)) {
setActiveCat("all");
}
}, [activeCategories]);
// sync external query (e.g. nav links)
useEffect(() => {
if (query.cat) setActiveCat(query.cat);
if (query.brand) setSelBrands([query.brand]);
}, [query.cat, query.brand]);
useEffect(() => { setPage(1); }, [activeCat, selBrands, selFinishes, selColors, gsmMin, gsmMax]);
const toggleSet = (set, setSetter, val) => {
setSetter(set.includes(val) ? set.filter(x => x !== val) : [...set, val]);
};
// ----- Apply filters -----
const filtered = useMemo(() => {
const cat = activeCategories.find(c => c.id === activeCat) || activeCategories[0];
let list = activePapers.filter(p => cat.match(p));
if (selBrands.length) list = list.filter(p => selBrands.includes(p.brand));
if (selFinishes.length) list = list.filter(p => selFinishes.includes(p.finish));
if (selColors.length) list = list.filter(p => selColors.includes(p.colorGroup));
list = list.filter(p => p.gsm >= gsmMin && p.gsm <= gsmMax);
list = list.filter(p => (p.price_min || 0) >= 100 || Object.keys(p.price || {}).length > 0);
if (sort === "price-asc") list = [...list].sort((a,b) => (a.price_min || 0) - (b.price_min || 0));
else if (sort === "price-desc") list = [...list].sort((a,b) => (b.price_min || 0) - (a.price_min || 0));
else if (sort === "gsm-asc") list = [...list].sort((a,b) => a.gsm - b.gsm);
else if (sort === "gsm-desc") list = [...list].sort((a,b) => b.gsm - a.gsm);
else if (sort === "featured") list = [...list].sort((a,b) => (b.featured?1:0) - (a.featured?1:0));
return list;
}, [activePapers, activeCat, selBrands, selFinishes, selColors, gsmMin, gsmMax, sort]);
const visiblePapers = filtered.slice(0, page * PAGE_SIZE);
const hasMore = filtered.length > page * PAGE_SIZE;
// ----- Counts per category -----
const catCounts = useMemo(() => {
const o = {};
activeCategories.forEach(c => { o[c.id] = activePapers.filter(p => c.match(p)).length; });
return o;
}, [activePapers, activeCategories]);
// ----- Active filter chips -----
const activeChips = [];
if (activeCat !== "all" && activeCategories.find(c=>c.id===activeCat)) activeChips.push({ label: activeCategories.find(c=>c.id===activeCat).label, clear: () => setActiveCat("all") });
selBrands.forEach(b => activeChips.push({ label: b, clear: () => setSelBrands(selBrands.filter(x => x !== b)) }));
selFinishes.forEach(f => activeChips.push({ label: f, clear: () => setSelFinishes(selFinishes.filter(x => x !== f)) }));
selColors.forEach(c => activeChips.push({ label: COLOR_GROUPS.find(g => g.id === c).label, clear: () => setSelColors(selColors.filter(x => x !== c)) }));
if (gsmMin !== 60 || gsmMax !== 350) activeChips.push({ label: `${gsmMin}–${gsmMax}gsm`, clear: () => { setGsmMin(60); setGsmMax(350); } });
const clearAll = () => {
setActiveCat("all"); setSelBrands([]); setSelFinishes([]); setSelColors([]); setGsmMin(60); setGsmMax(350);
};
const filterPos = tweaks.filterPos || "left-rail";
const gridType = (catalog && catalog[0] && catalog[0].acc_id) ? "uniform" : (tweaks.gridType || "editorial");
const density = tweaks.density || "default";
// Hide sidebar on mobile — accordion handles filters there
const [windowW, setWindowW] = useState(typeof window !== "undefined" ? window.innerWidth : 1200);
useEffect(() => {
const onResize = () => setWindowW(window.innerWidth);
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, [activePapers, activeCategories]);
const isMobile = windowW <= 980;
const sidebarVisible = filterPos === "left-rail" && !isMobile;
return (
{/* -------- HERO -------- */}
{catalogLoading && (
Memuat catalog…
)}
{catalogError && !catalogLoading && (
Menggunakan data lokal.
)}
{/* -------- Top filter bar (categories) -------- */}
Browse
{activeCategories.map(c => (
setActiveCat(c.id)}
>
{c.label}
{catCounts[c.id]}
))}
{filterPos === "top-bar" && (
setDrawerOpen(true)}>
More filters
)}
{/* -------- Mobile filter accordion -------- */}
setMobileAccOpen(o => !o)}>
Filter
{(() => { const n = selBrands.length + selFinishes.length + selColors.length + (gsmMin !== 60 || gsmMax !== 350 ? 1 : 0); return n > 0 ? {n} : null; })()}
+
Brand
{BRANDS.map(b => toggleSet(selBrands, setSelBrands, b)}>{b} )}
Finish
{FINISHES.map(f => toggleSet(selFinishes, setSelFinishes, f)}>{f} )}
Warna
{COLOR_GROUPS.map(g => toggleSet(selColors, setSelColors, g.id)} title={g.label} />)}
{(selBrands.length + selFinishes.length + selColors.length + (gsmMin !== 60 || gsmMax !== 350 ? 1 : 0)) > 0 && (
Reset filter
)}
{/* -------- Main gallery body -------- */}
{sidebarVisible && (
setDrawerOpen(false)}
showCloseBtn={drawerOpen}
/>
)}
{/* Toolbar */}
{filtered.length} kertas
{activeCat !== "all" && activeCategories.find(c=>c.id===activeCat) && <> · {activeCategories.find(c=>c.id===activeCat)?.label} >}
Sort
setSort(e.target.value)}>
Featured
Price · low to high
Price · high to low
Weight · light to heavy
Weight · heavy to light
{/* Active filter chips */}
{activeChips.length > 0 && (
{activeChips.map((ch, i) => (
{ch.label}
))}
Clear all
)}
{/* Grid */}
{filtered.length === 0 ? (
) : (
{visiblePapers.map(p => (
navigate("/product/" + p.id)}
onAddCart={() => addToCart(p)}
onToggleCompare={() => toggleCompare(p)}
isComparing={compare.some(x => x.id === p.id)}
onRequestSample={(paper) => { addSampleToList(paper); navigate("/sample-request"); }}
/>
))}
)}
{hasMore && (
setPage(p => p + 1)}
style={{minWidth:200}}
>
Load more — {filtered.length - page * PAGE_SIZE} kertas lagi
)}
{/* -------- Featured Fedrigoni banner -------- */}
{/* -------- Sample kit strip -------- */}
);
}
// helper for "request sample" cta from card — stash in localStorage
function addSampleToList(paper) {
try {
const cur = JSON.parse(localStorage.getItem("ip_samples") || "[]");
if (!cur.find(x => x.id === paper.id)) {
cur.push({ id: paper.id, brand: paper.brand, name: paper.name, gsm: paper.gsm, finish: paper.finish, tone: paper.swatchTone });
localStorage.setItem("ip_samples", JSON.stringify(cur));
}
} catch (e) {}
}
// ============================================================
// HERO
// ============================================================
const HERO_WORDS = ["paper.", "texture.", "kraft.", "bamboo.", "felt.", "letterpress.", "arcoprint.", "swatch.", "premium."];
function TypedWord() {
const prefersReduced = useRef(
typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
);
const [display, setDisplay] = useState("paper.");
const [phase, setPhase] = useState("hold");
const [idx, setIdx] = useState(0);
useEffect(() => {
let timeout;
const word = HERO_WORDS[idx];
if (phase === "hold") {
timeout = setTimeout(() => setPhase("delete"), 1800);
} else if (phase === "delete") {
if (display.length > 0) {
timeout = setTimeout(() => setDisplay(d => d.slice(0, -1)), 55);
} else {
const next = (idx + 1) % HERO_WORDS.length;
setIdx(next);
setPhase("type");
}
} else if (phase === "type") {
const target = HERO_WORDS[idx];
if (display.length < target.length) {
timeout = setTimeout(() => setDisplay(target.slice(0, display.length + 1)), 80);
} else {
setPhase("hold");
}
}
return () => clearTimeout(timeout);
}, [phase, display, idx]);
if (false && prefersReduced.current) {
return paper. ;
}
return (
{display}
);
}
function Hero() {
return (
{/* Dashed flight path — clipped dalam hero bounds */}
{/* Paper plane — posisi CSS, bukan SVG transform */}
Find
「 The Gallery 」 ペーパー
Specialty paper untuk designer, studio kreatif, dan brand yang peduli kualitas. Pre-cut A4 / A3 / SRA3 — MOQ 10 lembar.
302 papers in stock
8 categories
10 min order
);
}
// ============================================================
// BRAND VALUES STRIP
// ============================================================
function BrandValuesStrip() {
return (
✦
Pre-cut, siap pakai
A3+ pre-cut untuk semua kertas. Tidak perlu beli rim — order mulai 10 lembar.
◈
Brand premium dunia
Mohawk, Conqueror, Fedrigoni, Antalis — kertas yang sama dipakai studio terbaik di Eropa dan Amerika.
◎
Jakarta, kirim cepat
Stok tersedia di Jakarta. Jabodetabek 1–2 hari kerja, luar kota 3–5 hari.
);
}
// ============================================================
// FEATURED COLLECTIONS
// ============================================================
function FeaturedCollections({ navigate }) {
const collections = [
{
id: 'fancy',
label: 'Fancy Paper',
desc: 'Kertas premium dengan tekstur dan finishing eksklusif — untuk karya yang benar-benar berkesan.',
tone: '#F4EFE3',
grain: 'felt',
cat: 'fancy',
count: '133 produk',
},
{
id: 'creative',
label: 'Creative Paper',
desc: 'Pilihan kreatif untuk desainer — warna, tekstur, dan permukaan yang tidak biasa.',
tone: '#E8E0D0',
grain: 'smooth',
cat: 'creative',
count: '51 produk',
},
{
id: 'standard',
label: 'Standard Paper',
desc: 'Kertas berkualitas untuk kebutuhan sehari-hari studio — reliable, konsisten, harga terjangkau.',
tone: '#F0EBE0',
grain: 'smooth',
cat: 'standard',
count: '21 produk',
},
{
id: 'eco',
label: 'Eco Series',
desc: 'Kertas ramah lingkungan — FSC certified, bamboo fiber, recycled content.',
tone: '#E8EDE0',
grain: 'fiber',
cat: 'eco',
count: 'Eco certified',
eco: true,
},
];
return (
「 Collections 」
Browse by category
{collections.map(col => (
navigate('/gallery?cat=' + col.cat)}
>
{col.eco && (
Eco · FSC
)}
{col.label}
{col.desc}
{col.count}
→
))}
);
}
// ============================================================
// BRAND SPOTLIGHT — Mohawk
// ============================================================
function BrandSpotlight({ navigate }) {
return (
「 Featured Brand 」
The MohawkCollection
Pabrik kertas premium independen terbesar di Amerika Utara. Mohawk Via Felt, Superfine, Loop — kertas yang sama dipakai studio dan desainer terbaik di seluruh dunia, kini tersedia di Jakarta mulai 10 lembar.
25+ produk Mohawk
90–300 gsm range
USA origin
navigate('/gallery?brand=Mohawk')}
>
Browse Mohawk →
);
}
// ============================================================
// FILTER PANEL
// ============================================================
function FilterPanel({
selBrands, setSelBrands, toggleSet,
selFinishes, setSelFinishes,
selColors, setSelColors,
gsmMin, setGsmMin, gsmMax, setGsmMax,
onClose, showCloseBtn,
}) {
return (
<>
{showCloseBtn && (
Filters
Close
)}
Brand
{BRANDS.map(b => (
toggleSet(selBrands, setSelBrands, b)} />
{b}
{PAPERS.filter(p => p.brand === b).length}
))}
Finish
{FINISHES.map(f => (
toggleSet(selFinishes, setSelFinishes, f)} />
{f}
{PAPERS.filter(p => p.finish === f).length}
))}
Color
{COLOR_GROUPS.map(g => (
toggleSet(selColors, setSelColors, g.id)}
/>
))}
Print method
Offset 9
Letterpress 5
Foil Stamping 4
Digital 10
>
);
}
// ============================================================
// EMPTY STATE
// ============================================================
function EmptyState({ clearAll }) {
return (
Tidak ada kertas yang cocok.
Coba longgarkan filter — atau biarkan kami pilihkan.
Reset filters
);
}
// ============================================================
// FEATURED FEDRIGONI BANNER
// ============================================================
function FedrigoniBanner({ navigate }) {
return (
{/* corner sticker */}
SINCE 1888 · ITALIA
Featured Collection
The Fedrigoni Collection
Lebih dari 130 tahun, Fedrigoni adalah standar emas kertas Italia. Dari Arcoprint sampai Sirio Pearl — sekarang tersedia di Jakarta, dipotong sesuai kebutuhan, mulai 10 lembar.
navigate("/gallery?brand=Fedrigoni")}>Browse Fedrigoni
navigate("/sample-request")}>Request swatchbook
6 series in stock
30+ colorways
90–350 gsm range
);
}
// ============================================================
// SAMPLE KIT STRIP
// ============================================================
function SampleKitStrip({ navigate }) {
return (
「 Free · Gratis 」
Belum yakin kertas yang mana? Pesan sample kit.
Kami kirim 8 lembar kertas pilihan kami (atau pilihan Anda) — gratis ongkir Jakarta, tinggal ditimang, dipegang, dicoba print sendiri.
navigate("/sample-request")}>
Request a sample kit
);
}
Object.assign(window, {
GalleryPage,
Hero, FilterPanel, FedrigoniBanner, SampleKitStrip,
BrandValuesStrip, FeaturedCollections, BrandSpotlight,
addSampleToList,
});