- 태그 AND 검색 도입
- UI/UX 디자인 개선 (칩 & 배지)
- 모바일 최적화 및 레이아웃
- 성능 및 리소스 최적화 (Zero-Dependency 아이콘)
- 데이터 안정성 및 기타
- 그 외 오류 복구
- Tailwind CDN 제거
This commit is contained in:
2026-02-12 17:25:56 +09:00
parent a7817d2113
commit 555321fe70
10 changed files with 990 additions and 428 deletions

View File

@@ -25,69 +25,10 @@
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>sori.inventory</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="/style/tailwind.css" rel="stylesheet" />
<!-- Google Fonts: Inter -->
<link href="https://fonts.googleapis.com" rel="preconnect" />
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect" />
<!-- Material Symbols -->
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet" />
<script id="tailwind-config">
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
"primary": "#137fec",
"background-light": "#f6f7f8",
"background-dark": "#101922",
},
fontFamily: {
"display": ["Inter", "sans-serif"]
},
borderRadius: {
"DEFAULT": "0.25rem",
"lg": "0.5rem",
"xl": "0.75rem",
"full": "9999px"
},
screens: {
'xs': '480px',
},
aspectRatio: {
'card': '4 / 5',
},
},
},
}
</script>
<style>
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
#modal-main-carousel-container {
scroll-snap-type: none;
overflow-x: hidden;
}
.modal-open {
overflow: hidden !important;
height: 100% !important;
overscroll-behavior: none !important;
}
#product-modal {
overscroll-behavior: contain;
}
[id^="thumb-"] {
transition: all 0.6s ease-in-out;
background-color: #f1f5f9; /* 로딩 중 배경색 (slate-100) */
}
</style>
</head>
<body class="bg-background-light dark:bg-background-dark transition-colors duration-200">
<div class="relative flex h-auto min-h-screen w-full flex-col group/design-root overflow-x-hidden">
@@ -96,25 +37,31 @@
<header class="sticky top-0 z-50 gap-2 flex items-center justify-between whitespace-nowrap border-b border-solid border-slate-200 dark:border-slate-800 bg-white/80 dark:bg-background-dark/80 backdrop-blur-md px-6 md:px-40 py-3">
<div class="flex items-center gap-8">
<div class="flex items-center gap-3 text-slate-900 dark:text-white">
<div class="flex items-center justify-center size-8 bg-primary rounded-lg text-white">
<span class="material-symbols-outlined text-xl">inventory_2</span>
</div>
<img src="/images/assets/favicon/favicon-32x32.png" alt="sori.inventory logo" class="w-5 h-5" />
<h2 class="hidden xs:block text-lg font-bold leading-tight tracking-tight cursor-pointer" id="logo-title">sori.inventory</h2>
</div>
</div>
<div class="flex flex-1 justify-end gap-4 items-center">
<label class="flex flex-col min-w-40 h-10 max-w-64">
<div class="flex w-full flex-1 items-stretch rounded-lg h-full border border-slate-200 dark:border-slate-700">
<div class="text-slate-400 flex items-center justify-center pl-3">
<span class="material-symbols-outlined text-[20px]">search</span>
<div class="inset-y-0 left-0 pl-4 flex items-center pointer-events-none text-slate-400 group-focus-within:text-primary transition-colors">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</div>
<input id="search-input" class="form-input flex w-full min-w-0 flex-1 border-none bg-transparent focus:outline-0 focus:ring-0 text-slate-900 dark:text-slate-100 placeholder:text-slate-400 px-3 text-sm font-normal" placeholder="검색어 입력..." value="" />
</div>
</label>
<button id="theme-toggle" class="flex items-center justify-center size-10 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors">
<span id="theme-toggle-dark-icon" class="material-symbols-outlined hidden">dark_mode</span>
<span id="theme-toggle-light-icon" class="material-symbols-outlined hidden">light_mode</span>
</button>
<div class="flex items-center gap-2 md:gap-3">
<button id="theme-toggle" class="p-2 rounded-xl bg-slate-100 dark:bg-slate-800 text-slate-500 dark:text-slate-400 hover:bg-slate-200 dark:hover:bg-slate-700 transition-all">
<svg id="theme-toggle-light-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"></path>
</svg>
<svg id="theme-toggle-dark-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
</button>
</div>
</div>
</header>
<main class="flex-1">
@@ -134,6 +81,24 @@
<h4 class="text-xs font-semibold uppercase tracking-wider text-slate-500">Status</h4>
<div id="status-chips" class="flex flex-wrap gap-2"></div>
</div>
<!-- 태그 -->
<div class="mb-8">
<div class="flex items-center justify-between mb-3">
<h3 class="text-slate-900 dark:text-white text-sm font-bold flex items-center gap-2">
태그 필터
<span id="active-tag-count" class="text-[10px] bg-primary/10 text-primary px-1.5 py-0.5 rounded-full hidden">0</span>
</h3>
<button id="toggle-tags" class="text-slate-400 hover:text-primary transition-transform duration-300 origin-center">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="m6 9 6 6 6-6" />
</svg>
</button>
</div>
<div id="tag-container" class="relative overflow-hidden transition-all duration-300 max-h-8">
<div id="tag-chips" class="flex flex-wrap gap-1.5"></div>
</div>
</div>
</div>
</section>
<!-- Filters/Chips Section -->
@@ -145,38 +110,61 @@
</div>
<section class="px-6 md:px-40 pt-6 -mb-4">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<div class="flex flex-wrap items-center justify-between gap-y-4">
<div class="flex items-center gap-2 order-1">
<span class="text-slate-400 dark:text-slate-500 text-xs font-bold uppercase tracking-widest">Total Results</span>
<span id="total-count" class="px-2 py-0.5 rounded bg-primary/10 text-primary text-sm font-bold">0</span>
</div>
<div id="selection-summary" class="hidden items-center gap-3 bg-primary/10 px-3 py-1.5 rounded-full border border-primary/20">
<span class="text-[11px] font-bold text-primary">
<span id="selected-count">0</span>
items
</span>
<div class="w-px h-3 bg-primary/30"></div>
<span class="text-[11px] font-bold text-primary" id="selected-total-price">₩0</span>
<div class="flex items-center gap-1 ml-2">
<div class="flex bg-slate-100 dark:bg-slate-800 p-1 rounded-lg order-2 md:order-3">
<button id="view-grid" class="p-1.5 rounded-md text-slate-400 cursor-pointer hover:text-slate-600 dark:hover:text-slate-200 transition-all">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
</button>
<button id="view-table" class="p-1.5 rounded-md text-slate-400 cursor-pointer hover:text-slate-600 dark:hover:text-slate-200 transition-all">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="8" y1="6" x2="21" y2="6"></line>
<line x1="8" y1="12" x2="21" y2="12"></line>
<line x1="8" y1="18" x2="21" y2="18"></line>
<line x1="3" y1="6" x2="3.01" y2="6"></line>
<line x1="3" y1="12" x2="3.01" y2="12"></line>
<line x1="3" y1="18" x2="3.01" y2="18"></line>
</svg>
</button>
</div>
<div id="selection-summary" class="hidden items-center gap-3 bg-primary/10 px-3 py-1.5 rounded-full border border-primary/20 w-full md:w-auto justify-between md:justify-start order-3 md:order-2">
<div class="flex items-center gap-2">
<span class="text-[11px] font-bold text-primary">
<span id="selected-count">0</span>
items
</span>
<div class="w-px h-3 bg-primary/30"></div>
<span class="text-[11px] font-bold text-primary" id="selected-total-price">₩0</span>
</div>
<div class="flex items-center gap-1">
<button onclick="window.exportToExcel()" class="flex items-center gap-1 bg-primary text-white text-[10px] px-2 py-1 rounded-md hover:bg-primary-dark transition-colors">
<span class="material-symbols-outlined text-sm">download</span>
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Export
</button>
<button onclick="window.resetSelection()" class="flex items-center gap-1 bg-white dark:bg-slate-800 text-slate-500 text-[10px] px-2 py-1 rounded-md border border-slate-200 dark:border-slate-700 hover:bg-slate-50 transition-colors">
<span class="material-symbols-outlined text-sm">restart_alt</span>
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path>
<path d="M3 3v5h5"></path>
</svg>
Reset
</button>
</div>
</div>
<div class="flex bg-slate-100 dark:bg-slate-800 p-1 rounded-lg">
<button id="view-grid" class="p-1.5 rounded-md bg-white dark:bg-slate-700 shadow-sm text-primary transition-all">
<span class="material-symbols-outlined text-xl">grid_view</span>
</button>
<button id="view-table" class="p-1.5 rounded-md text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 transition-all">
<span class="material-symbols-outlined text-xl">format_list_bulleted</span>
</button>
</div>
</div>
</section>
<!-- Table -->
@@ -210,12 +198,15 @@
</main>
<!-- Modal -->
<!-- Backdrop Overlay -->
<div id="product-modal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/60 backdrop-blur-sm p-4 md:p-10" onclick="if(event.target === this) closeModal()">
<div id="product-modal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/60 backdrop-blur-sm p-4 md:p-10" onclick="if (event.target === this) closeModal();">
<!-- Modal Container: 모바일에서 화면 안에 맞춤 (dvh), 이미지 영역 높이 제한 -->
<div class="relative w-full max-w-6xl max-h-[calc(100dvh-2rem)] md:max-h-[90vh] bg-white dark:bg-background-dark rounded-xl shadow-2xl overflow-hidden flex flex-col md:flex-row border border-gray-200 dark:border-gray-800 mx-auto">
<!-- Close Button -->
<button onclick="closeModal()" class="absolute top-4 right-4 z-50 flex items-center justify-center w-10 h-10 rounded-full bg-white/80 dark:bg-gray-800/80 backdrop-blur hover:bg-white dark:hover:bg-gray-700 transition-colors shadow-sm">
<span class="material-symbols-outlined text-gray-900 dark:text-white">close</span>
<button onclick="window.closeModal()" class="absolute top-4 right-4 z-40 p-2 rounded-full bg-white/80 dark:bg-slate-900/80 backdrop-blur-md border border-slate-200 dark:border-slate-700 hover:bg-slate-100 dark:hover:bg-slate-800 transition-all shadow-sm group">
<svg class="w-5 h-5 text-gray-900 dark:text-white transition-transform group-hover:rotate-90" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
<!-- Left: Gallery Section (모바일: 상하좌우 패딩으로 이미지 영역 확실히) -->
<div class="w-full md:w-3/5 flex flex-col relative overflow-hidden min-h-0 max-h-[40dvh] md:max-h-none p-3 md:p-0">
@@ -228,7 +219,9 @@
<div
class="w-full h-full bg-center bg-no-repeat bg-contain rounded-lg"
data-alt="Front view of Sony WH-1000XM5 headphones in black"
style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuCOlUeqiUla7LkapcfcrnlTRF-bvdnD2tGTS8zjFgnxr6MNEwmTehjldThx6SCikcIP4-uUK4fbm1EFxj7XKKckJnQr8AunGsdWdgOlH5Fex0ML3uFw5fCnp997fuXQa2ceXdqXfiGDM17AdqB7tx9kxLoxEUXDxROSiH7I2-KFOweIR1-dBljEbeih1fQ1y_HACne_STdXKwGfrkPsHOXz-Ls7-MBW8uD1i5Mz64d3I8z4sO036qIlTNd2Iz4pqPxp7ucNkhrtQoc");'></div>
style="
background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuCOlUeqiUla7LkapcfcrnlTRF-bvdnD2tGTS8zjFgnxr6MNEwmTehjldThx6SCikcIP4-uUK4fbm1EFxj7XKKckJnQr8AunGsdWdgOlH5Fex0ML3uFw5fCnp997fuXQa2ceXdqXfiGDM17AdqB7tx9kxLoxEUXDxROSiH7I2-KFOweIR1-dBljEbeih1fQ1y_HACne_STdXKwGfrkPsHOXz-Ls7-MBW8uD1i5Mz64d3I8z4sO036qIlTNd2Iz4pqPxp7ucNkhrtQoc');
"></div>
</div>
</div>
</div>
@@ -284,12 +277,17 @@
<!-- Footer / CTA: 항상 하단 고정, 버튼이 설명 위로 겹치지 않음 -->
<div class="flex flex-col gap-2 shrink-0 pt-4 pb-6 px-4 sm:px-6 md:px-8 lg:px-10 border-t border-gray-100 dark:border-gray-800 bg-white dark:bg-background-dark">
<button id="copy-link-btn" class="w-full flex items-center justify-center gap-2 bg-slate-900 dark:bg-white dark:text-slate-900 text-white font-bold py-4 px-6 rounded-xl transition-all shadow-lg">
<span class="material-symbols-outlined">link</span>
<svg class="w-4 h-4 transition-transform group-hover:rotate-12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
</svg>
<span id="copy-btn-text">상품 링크 복사하기</span>
</button>
<a href="https://open.kakao.com/o/sPZ3Cnfi" target="_blank" class="w-full flex items-center justify-center gap-2 bg-[#fae100] text-[#3c1e1e] font-bold py-4 px-6 rounded-xl transition-all shadow-lg hover:bg-[#f7d600]">
<span class="material-symbols-outlined">chat_bubble</span>
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
<span>오픈카톡으로 문의하기</span>
</a>
@@ -299,14 +297,22 @@
</div>
</div>
<!-- selection-reset-modal -->
<div id="selection-reset-modal" class="fixed inset-0 z-[100] hidden items-center justify-center p-4 bg-slate-900/60 backdrop-blur-sm">
<div id="selection-reset-modal" class="fixed inset-0 z-50 hidden items-center justify-center p-4 bg-slate-900/60 backdrop-blur-sm">
<div class="bg-white dark:bg-slate-900 rounded-2xl p-6 max-w-sm w-full shadow-2xl scale-95 transition-transform duration-200">
<div class="flex flex-col items-center text-center">
<div class="size-12 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center mb-4">
<span class="material-symbols-outlined text-red-600">delete_sweep</span>
<span class="text-red-600">
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor">
<path d="M600-240v-80h160v80H600Zm0-320v-80h280v80H600Zm0 160v-80h240v80H600ZM120-640H80v-80h160v-60h160v60h160v80h-40v360q0 33-23.5 56.5T440-200H200q-33 0-56.5-23.5T120-280v-360Zm80 0v360h240v-360H200Zm0 0v360-360Z" />
</svg>
</span>
</div>
<h3 class="text-lg font-bold text-slate-900 dark:text-white mb-2">선택 내역 초기화</h3>
<p class="text-slate-500 dark:text-slate-400 text-sm mb-6">현재 체크된 모든 상품의 선택이 해제됩니다.<br>계속하시겠습니까?</p>
<p class="text-slate-500 dark:text-slate-400 text-sm mb-6">
현재 체크된 모든 상품의 선택이 해제됩니다.
<br />
계속하시겠습니까?
</p>
<div class="flex gap-3 w-full">
<button onclick="closeSelectionResetModal()" class="flex-1 py-3 px-4 rounded-xl font-semibold text-slate-600 dark:text-slate-400 bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 transition-colors">취소</button>
<button onclick="confirmSelectionReset()" class="flex-1 py-3 px-4 rounded-xl font-semibold text-white bg-red-500 hover:bg-red-600 transition-colors">초기화</button>