diff --git a/app/services/website/samples/shopping/page.tsx b/app/services/website/samples/shopping/page.tsx index 7cb2885..3c2ba21 100644 --- a/app/services/website/samples/shopping/page.tsx +++ b/app/services/website/samples/shopping/page.tsx @@ -1,714 +1,717 @@ 'use client'; import Link from 'next/link'; -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect } from 'react'; -/* ═══════════════════════════════════════════ +/* ═══════════════════════════════════════════════════ + DESIGN TOKEN — 인테리어 페이지와 완전히 다른 팔레트 + No gold, no cream, no warm amber. + Editorial B&W + cool stone. +═══════════════════════════════════════════════════ */ +const T = { + ink: '#0C0B09', + paper: '#F4F2EF', + sand: '#E9E5DF', + stone: '#7C7870', + chalk: '#B8B3AB', + white: '#FAFAF8', +} as const; + +const BANNER_H = 40; +const NAV_H = 56; + +/* ═══════════════════════════════════════════════════ + UNSPLASH FASHION IMAGES +═══════════════════════════════════════════════════ */ +const IMG = { + hero: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?auto=format&fit=crop&w=900&q=85', + p1: 'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?auto=format&fit=crop&w=600&q=80', + p2: 'https://images.unsplash.com/photo-1509631179647-0177331693ae?auto=format&fit=crop&w=600&q=80', + p3: 'https://images.unsplash.com/photo-1445205170230-053b83016050?auto=format&fit=crop&w=600&q=80', + p4: 'https://images.unsplash.com/photo-1487222477894-8943e31ef7b2?auto=format&fit=crop&w=600&q=80', + p5: 'https://images.unsplash.com/photo-1469334031218-e382a71b716b?auto=format&fit=crop&w=600&q=80', + p6: 'https://images.unsplash.com/photo-1434389677669-e08b4cac3105?auto=format&fit=crop&w=600&q=80', + p7: 'https://images.unsplash.com/photo-1503342564405-cf0c5f9d5c79?auto=format&fit=crop&w=600&q=80', + p8: 'https://images.unsplash.com/photo-1525507119028-ed4ccf09e210?auto=format&fit=crop&w=600&q=80', + edit1: 'https://images.unsplash.com/photo-1490481651871-ab68de25d43d?auto=format&fit=crop&w=1400&q=85', + feat1: 'https://images.unsplash.com/photo-1558171813-0022efd9b40d?auto=format&fit=crop&w=700&q=80', + feat2: 'https://images.unsplash.com/photo-1542060748-10c28b62716f?auto=format&fit=crop&w=700&q=80', + lb1: 'https://images.unsplash.com/photo-1441986300917-64674bd600d8?auto=format&fit=crop&w=500&q=80', + lb2: 'https://images.unsplash.com/photo-1469334031218-e382a71b716b?auto=format&fit=crop&w=500&q=80', + lb3: 'https://images.unsplash.com/photo-1503342564405-cf0c5f9d5c79?auto=format&fit=crop&w=500&q=80', + lb4: 'https://images.unsplash.com/photo-1434389677669-e08b4cac3105?auto=format&fit=crop&w=500&q=80', + lb5: 'https://images.unsplash.com/photo-1525507119028-ed4ccf09e210?auto=format&fit=crop&w=500&q=80', +} as const; + +/* ═══════════════════════════════════════════════════ DATA -═══════════════════════════════════════════ */ -type Category = '전체' | '의류' | '액세서리' | '라이프'; +═══════════════════════════════════════════════════ */ +type Cat = '전체' | '아우터' | '상의' | '하의' | '액세서리'; const products = [ - { id: 1, name: 'Linen Over Shirt', sub: '린넨 오버셔츠', price: 89000, tag: '신상', cat: '의류' as Category, seed: 'mel01', badge: 'NEW' }, - { id: 2, name: 'Capsule Tote Bag', sub: '카프슐 토트백', price: 145000, tag: '베스트', cat: '액세서리' as Category, seed: 'mel02' }, - { id: 3, name: 'Wide Crop Pants', sub: '와이드 크롭 팬츠', price: 97000, cat: '의류' as Category, seed: 'mel03' }, - { id: 4, name: 'Knit Cardigan', sub: '니트 카디건', price: 125000, cat: '의류' as Category, seed: 'mel04', badge: 'BEST' }, - { id: 5, name: 'Ceramic Mug Set', sub: '세라믹 머그 세트', price: 58000, cat: '라이프' as Category, seed: 'mel05' }, - { id: 6, name: 'Brass Ring Set', sub: '브라스 링 세트', price: 42000, cat: '액세서리' as Category, seed: 'mel06' }, - { id: 7, name: 'Minimal Slip Dress', sub: '미니멀 슬립 드레스', price: 112000, cat: '의류' as Category, seed: 'mel07', badge: 'NEW' }, - { id: 8, name: 'Linen Cushion Cover', sub: '린넨 쿠션 커버', price: 35000, cat: '라이프' as Category, seed: 'mel08' }, + { id: 1, name: 'Structured Blazer', kr: '스트럭처드 블레이저', price: 328000, cat: '아우터' as Cat, img: IMG.p1, isNew: true }, + { id: 2, name: 'Cocoon Coat', kr: '코쿤 코트', price: 498000, cat: '아우터' as Cat, img: IMG.p2, isBest: true }, + { id: 3, name: 'Slip Midi Dress', kr: '슬립 미디 드레스', price: 218000, cat: '상의' as Cat, img: IMG.p3, isNew: true }, + { id: 4, name: 'Relaxed Oxford', kr: '릴랙스드 옥스포드', price: 145000, cat: '상의' as Cat, img: IMG.p4 }, + { id: 5, name: 'Wide Trousers', kr: '와이드 트라우저', price: 185000, cat: '하의' as Cat, img: IMG.p5 }, + { id: 6, name: 'Barrel Denim', kr: '배럴 데님', price: 198000, cat: '하의' as Cat, img: IMG.p6, isBest: true }, + { id: 7, name: 'Leather Mini Bag', kr: '레더 미니백', price: 278000, cat: '액세서리' as Cat, img: IMG.p7, isNew: true }, + { id: 8, name: 'Square Sunglasses', kr: '스퀘어 선글라스', price: 128000, cat: '액세서리' as Cat, img: IMG.p8 }, ]; const reviews = [ - { name: '김민서', location: '서울', rating: 5, text: '소재부터 다릅니다. 린넨 셔츠 착용감이 정말 좋아요. 세탁 후에도 형태 유지가 잘 돼서 재구매할 것 같아요.', product: 'Linen Over Shirt' }, - { name: '이하은', location: '부산', rating: 5, text: '미니멀한 디자인인데 어디에나 잘 어울립니다. 포장도 너무 예쁘게 해주셔서 선물 받은 기분이었어요.', product: 'Capsule Tote Bag' }, - { name: '박지수', location: '대구', rating: 5, text: '세라믹 머그 세트 배송이 빠르고 파손 없이 왔어요. 질감이 정말 고급스러워서 매일 아침 쓰고 있어요.', product: 'Ceramic Mug Set' }, + { quote: '입고 나서 동료가 어디서 샀냐고 계속 물어봐요. 핏이 정말 완벽합니다.', name: '하윤서', city: '서울', item: 'Structured Blazer' }, + { quote: '코쿤 코트, 사진보다 실물이 훨씬 좋아요. 소재감이 기대 이상입니다.', name: '이서진', city: '제주', item: 'Cocoon Coat' }, + { quote: '슬립 드레스를 받았는데 바느질 하나하나에서 정성이 느껴져요.', name: '박도현', city: '부산', item: 'Slip Midi Dress' }, ]; -const storyImages = [ - { seed: 'story1', aspect: '3/4' }, - { seed: 'story2', aspect: '1/1' }, - { seed: 'story3', aspect: '3/4' }, -]; - -/* ═══════════════════════════════════════════ - CONSTANTS -═══════════════════════════════════════════ */ -const BANNER_H = 40; -const NAV_H = 64; - -const BEIGE = '#FAFAF8'; -const DARK = '#1A1A1A'; -const TAUPE = '#8B7355'; -const MUTED = '#B5A898'; -const SOFT = '#F3EFE9'; -const WARM = '#C4A882'; - -/* ═══════════════════════════════════════════ - SVG ICONS -═══════════════════════════════════════════ */ -const CartIcon = () => ( - - - - -); -const HeartIcon = ({ filled = false }: { filled?: boolean }) => ( - - - -); -const SearchIcon = () => ( - - - -); -const StarFull = () => ( - - - -); - -/* ═══════════════════════════════════════════ - PRODUCT CARD -═══════════════════════════════════════════ */ -function ProductCard({ product, large = false }: { product: typeof products[0]; large?: boolean }) { - const [liked, setLiked] = useState(false); - const [hovered, setHovered] = useState(false); - - return ( -
setHovered(true)} - onMouseLeave={() => setHovered(false)} - > - {/* Image */} -
- {product.name} - {/* Overlay */} -
- - {/* Badge */} - {product.badge && ( -
{product.badge}
- )} - - {/* Like */} - - - {/* Quick add */} -
- -
-
- - {/* Info */} -
-

{product.cat}

-

{product.name}

-

{product.sub}

-

- {product.price.toLocaleString()}원 -

-
-
- ); -} - -/* ═══════════════════════════════════════════ +/* ═══════════════════════════════════════════════════ MAIN PAGE -═══════════════════════════════════════════ */ +═══════════════════════════════════════════════════ */ export default function ShoppingPage() { - const [activeCategory, setActiveCategory] = useState('전체'); - const [scrolled, setScrolled] = useState(false); - const [showTop, setShowTop] = useState(false); - const [cartCount, setCartCount] = useState(0); + const [activeCat, setActiveCat] = useState('전체'); + const [scrolled, setScrolled] = useState(false); + const [showTop, setShowTop] = useState(false); + const [cart, setCart] = useState(0); useEffect(() => { - const scroller: HTMLElement = + const sc: HTMLElement = (document.querySelector('.main-content') as HTMLElement | null) ?? document.documentElement; const onScroll = () => { - setScrolled(scroller.scrollTop > 60); - setShowTop(scroller.scrollTop > 400); + setScrolled(sc.scrollTop > 40); + setShowTop(sc.scrollTop > 500); }; - scroller.addEventListener('scroll', onScroll, { passive: true }); + sc.addEventListener('scroll', onScroll, { passive: true }); + /* reveal */ const obs = new IntersectionObserver( - (entries) => entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('ms-visible'); }), - { threshold: 0.08, root: scroller === document.documentElement ? null : scroller } + (entries) => entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('ml-in'); }), + { threshold: 0.07, root: sc === document.documentElement ? null : sc } ); - document.querySelectorAll('.ms-reveal').forEach(el => obs.observe(el)); + document.querySelectorAll('.ml-reveal').forEach(el => obs.observe(el)); return () => { - scroller.removeEventListener('scroll', onScroll); + sc.removeEventListener('scroll', onScroll); obs.disconnect(); }; }, []); - const categories: (Category | '전체')[] = ['전체', '의류', '액세서리', '라이프']; - const filtered = activeCategory === '전체' ? products : products.filter(p => p.cat === activeCategory); + const cats: (Cat | '전체')[] = ['전체', '아우터', '상의', '하의', '액세서리']; + const filtered = activeCat === '전체' ? products : products.filter(p => p.cat === activeCat); + + const scrollTop = () => { + const sc = (document.querySelector('.main-content') as HTMLElement | null) ?? document.documentElement; + sc.scrollTo({ top: 0, behavior: 'smooth' }); + }; return ( -
+
- {/* ── 폰트 + CSS ── */} + {/* ── FONTS + GLOBAL ── */}