added on local at 2026-07-01 13:48:01
| 1 | <!-- Category manager. Backed by either /categories.cgi (per-seller list) | |
| 2 | or /admin_categories.cgi (admin default list new sellers receive | |
| 3 | a copy of). $form_action + page header tvars switch between the | |
| 4 | two. --> | |
| 5 | ||
| 6 | <div class="page-head"> | |
| 7 | <div> | |
| 8 | <span class="page-eyebrow"><span class="dot"></span> $page_eyebrow</span> | |
| 9 | <h1 class="page-title">$page_title</h1> | |
| 10 | <p class="page-subtitle">$page_subtitle</p> | |
| 11 | </div> | |
| 12 | <div class="page-actions"> | |
| 13 | <button type="button" class="btn btn-primary" id="catOpenNew"> | |
| 14 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg> | |
| 15 | New category | |
| 16 | </button> | |
| 17 | </div> | |
| 18 | </div> | |
| 19 | ||
| 20 | [if:$has_flash] | |
| 21 | <div class="banner $flash_kind mb-3"> | |
| 22 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/></svg> | |
| 23 | <div class="text-sm fw-600">$flash_msg</div> | |
| 24 | </div> | |
| 25 | [/if] | |
| 26 | ||
| 27 | <style> | |
| 28 | /* Compact card: 3 rows -- full-width color banner with the category | |
| 29 | name, then description, then slug + action buttons on the bottom | |
| 30 | row floating right. The banner reads as a header strip so the | |
| 31 | card has a clear visual hierarchy at a glance. */ | |
| 32 | .cat-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(240px, 1fr)); gap:10px; } | |
| 33 | .cat-card { padding:0; border:1px solid var(--col-border); border-radius:10px; background:var(--col-surface-1); display:flex; flex-direction:column; overflow:hidden; } | |
| 34 | /* Banner stretches full card width. Rounded only on top because | |
| 35 | the card itself clips the corners (`overflow:hidden`). */ | |
| 36 | .cat-banner { display:flex; align-items:center; gap:6px; padding:8px 12px; font-size:13px; font-weight:700; color:#fff; min-width:0; } | |
| 37 | .cat-banner-name { min-width:0; flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } | |
| 38 | /* Emoji renders larger than the surrounding letters; a -1px vertical | |
| 39 | nudge centers it visually against the smaller glyphs. */ | |
| 40 | .cat-banner-emoji { font-size:16px; line-height:1; display:inline-block; transform:translateY(-1px); flex-shrink:0; } | |
| 41 | .cat-body { padding:8px 12px 10px; display:flex; flex-direction:column; gap:6px; } | |
| 42 | .cat-desc { font-size:12px; color:var(--col-text-2); line-height:1.4; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; min-height:1.4em; } | |
| 43 | .cat-foot { display:flex; align-items:center; justify-content:space-between; gap:8px; } | |
| 44 | .cat-meta { font-size:10px; color:var(--col-text-3); letter-spacing:0.5px; text-transform:uppercase; display:flex; gap:10px; align-items:center; min-width:0; overflow:hidden; } | |
| 45 | .cat-actions { display:flex; gap:4px; flex-shrink:0; } | |
| 46 | .cat-actions .btn { padding:3px 9px; font-size:11px; } | |
| 47 | .cat-empty { padding:60px 24px; text-align:center; border:1px dashed var(--col-border-2); border-radius:14px; color:var(--col-text-2); } | |
| 48 | ||
| 49 | /* Modal */ | |
| 50 | .cat-modal { position:fixed; inset:0; background:rgba(8,11,24,0.78); display:none; align-items:center; justify-content:center; z-index:9000; padding:20px; } | |
| 51 | .cat-modal.is-open { display:flex; } | |
| 52 | .cat-modal-card { background:var(--col-surface-1); border:1px solid var(--col-border-2); border-radius:14px; padding:24px 28px; width:min(520px, 100%); max-height:90vh; overflow-y:auto; } | |
| 53 | .cat-modal h2 { font-family:var(--font-display,inherit); font-size:22px; margin:0 0 6px; color:#fff; } | |
| 54 | .cat-modal .helper { font-size:13px; color:var(--col-text-3); margin:0 0 18px; } | |
| 55 | .cat-color-grid { display:flex; gap:8px; flex-wrap:wrap; align-items:center; } | |
| 56 | .cat-color-grid input[type=radio] { display:none; } | |
| 57 | .cat-color-grid label { width:28px; height:28px; border-radius:50%; border:2px solid transparent; cursor:pointer; transition:transform .12s; } | |
| 58 | .cat-color-grid input:checked + label { border-color:#fff; transform:scale(1.15); } | |
| 59 | .cat-color-custom-wrap.is-active { border-style:solid !important; border-color:#fff !important; transform:scale(1.15); } | |
| 60 | ||
| 61 | /* Emoji quick-pick grid. ~100 buttons, scrolls vertically so the | |
| 62 | modal stays compact. Custom thin scrollbar so it doesn't stand | |
| 63 | out against the modal surface. */ | |
| 64 | .cat-emoji-picker { display:flex; flex-wrap:wrap; gap:4px; margin-top:6px; padding:6px; background:var(--col-surface-2); border:1px solid var(--col-border); border-radius:8px; max-height:148px; overflow-y:auto; scrollbar-width:thin; scrollbar-color:var(--col-border-2) transparent; } | |
| 65 | .cat-emoji-picker::-webkit-scrollbar { width:8px; } | |
| 66 | .cat-emoji-picker::-webkit-scrollbar-thumb { background:var(--col-border-2); border-radius:4px; } | |
| 67 | .cat-emoji-picker::-webkit-scrollbar-track { background:transparent; } | |
| 68 | .cat-emoji-pick { width:32px; height:32px; padding:0; border:1px solid var(--col-border); background:var(--col-surface-1); border-radius:6px; font-size:18px; line-height:1; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; transition:background .12s,border-color .12s,transform .12s; color:var(--col-text); flex:0 0 auto; } | |
| 69 | .cat-emoji-pick:hover { background:var(--grad-brand-soft); border-color:var(--col-accent); transform:scale(1.08); } | |
| 70 | .cat-emoji-pick[data-clear="1"] { color:var(--col-text-3); font-size:14px; } | |
| 71 | </style> | |
| 72 | ||
| 73 | [if:!$has_cats] | |
| 74 | <div class="cat-empty"> | |
| 75 | <div style="font-size:48px;margin-bottom:8px">+</div> | |
| 76 | <h2 style="color:#fff;font-family:var(--font-display,inherit);margin-bottom:8px">No categories yet</h2> | |
| 77 | <p>Categories let you group listings on your storefront. Try <strong>"Halloween 2026"</strong>, <strong>"Patreon exclusive"</strong>, <strong>"Tabletop"</strong> — whatever organization your buyers will look for.</p> | |
| 78 | <button type="button" class="btn btn-primary" id="catOpenNewEmpty" style="margin-top:14px">Create your first category</button> | |
| 79 | </div> | |
| 80 | [/if] | |
| 81 | ||
| 82 | [if:$has_cats] | |
| 83 | <div class="cat-grid"> | |
| 84 | [loop:@cats] | |
| 85 | <div class="cat-card"> | |
| 86 | <div class="cat-banner" style="background:$loop1.color"> | |
| 87 | [if:$loop1.icon]<span class="cat-banner-emoji">$loop1.icon</span>[/if] | |
| 88 | <span class="cat-banner-name">$loop1.name</span> | |
| 89 | </div> | |
| 90 | <div class="cat-body"> | |
| 91 | <div class="cat-desc">$loop1.description</div> | |
| 92 | <div class="cat-foot"> | |
| 93 | <div class="cat-meta"> | |
| 94 | <span>/$loop1.slug</span> | |
| 95 | [if:$show_counts]<span>$loop1.model_count listing(s)</span>[/if] | |
| 96 | </div> | |
| 97 | <div class="cat-actions"> | |
| 98 | <button type="button" class="btn btn-secondary btn-sm cat-edit" | |
| 99 | data-id="$loop1.id" data-name="$loop1.name" data-desc="$loop1.description" | |
| 100 | data-color="$loop1.color" data-icon="$loop1.icon" data-sort="$loop1.sort_order" | |
| 101 | data-public="$loop1.is_public">Edit</button> | |
| 102 | <form method="POST" action="$form_action" style="display:inline" | |
| 103 | data-confirm-title="Delete this category?" | |
| 104 | data-confirm-message="Removing the category does not delete any listings. Tags pointing at it will simply drop from those listings." | |
| 105 | data-confirm-label="Delete" | |
| 106 | data-confirm-style="danger"> | |
| 107 | <input type="hidden" name="_act" value="delete"> | |
| 108 | <input type="hidden" name="id" value="$loop1.id"> | |
| 109 | <button type="submit" class="btn btn-danger btn-sm">Delete</button> | |
| 110 | </form> | |
| 111 | </div> | |
| 112 | </div> | |
| 113 | </div> | |
| 114 | </div> | |
| 115 | [/loop] | |
| 116 | </div> | |
| 117 | [/if] | |
| 118 | ||
| 119 | <!-- Create / edit modal --> | |
| 120 | <div class="cat-modal" id="catModal"> | |
| 121 | <div class="cat-modal-card"> | |
| 122 | <h2 id="catModalTitle">New category</h2> | |
| 123 | <p class="helper">Pick a chip color and an optional emoji. Both render on your storefront filter bar.</p> | |
| 124 | <form method="POST" action="$form_action" id="catForm"> | |
| 125 | <input type="hidden" name="_act" id="catAct" value="create"> | |
| 126 | <input type="hidden" name="id" id="catId" value=""> | |
| 127 | ||
| 128 | <div class="form-group"> | |
| 129 | <label class="form-label">Name <span class="req">*</span></label> | |
| 130 | <input type="text" name="name" id="catName" class="input" maxlength="120" required placeholder="e.g. Halloween 2026"> | |
| 131 | </div> | |
| 132 | ||
| 133 | <div class="form-group"> | |
| 134 | <label class="form-label">Description <span class="text-xs text-dim">(optional)</span></label> | |
| 135 | <input type="text" name="description" id="catDesc" class="input" maxlength="255" placeholder="One-line description for the chip tooltip"> | |
| 136 | </div> | |
| 137 | ||
| 138 | <div class="form-group"> | |
| 139 | <label class="form-label">Chip color</label> | |
| 140 | <div class="cat-color-grid" id="catColors"> | |
| 141 | <input type="radio" name="color_swatch" id="cc1" value="#6d28d9"><label for="cc1" style="background:#6d28d9"></label> | |
| 142 | <input type="radio" name="color_swatch" id="cc2" value="#1d4ed8"><label for="cc2" style="background:#1d4ed8"></label> | |
| 143 | <input type="radio" name="color_swatch" id="cc3" value="#047857"><label for="cc3" style="background:#047857"></label> | |
| 144 | <input type="radio" name="color_swatch" id="cc4" value="#b45309"><label for="cc4" style="background:#b45309"></label> | |
| 145 | <input type="radio" name="color_swatch" id="cc5" value="#b91c1c"><label for="cc5" style="background:#b91c1c"></label> | |
| 146 | <input type="radio" name="color_swatch" id="cc6" value="#be185d"><label for="cc6" style="background:#be185d"></label> | |
| 147 | <input type="radio" name="color_swatch" id="cc7" value="#0e7490"><label for="cc7" style="background:#0e7490"></label> | |
| 148 | <input type="radio" name="color_swatch" id="cc8" value="#334155"><label for="cc8" style="background:#334155"></label> | |
| 149 | <!-- Custom color: the rainbow tile is visible, the native | |
| 150 | color input sits transparently on top so the click hits | |
| 151 | the actual input (label-for tricks don't reliably open | |
| 152 | native color pickers in Chrome). --> | |
| 153 | <div class="cat-color-custom-wrap" title="Pick a custom color" | |
| 154 | style="position:relative;width:28px;height:28px;border-radius:50%;border:2px dashed var(--col-border-2);overflow:hidden;background:conic-gradient(from 0deg,#b91c1c,#b45309,#047857,#0e7490,#1d4ed8,#6d28d9,#be185d,#b91c1c);"> | |
| 155 | <span id="catCustomDot" style="display:none;position:absolute;inset:5px;border-radius:50%;border:2px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,0.25);background:#fff;pointer-events:none"></span> | |
| 156 | <input type="color" id="catColorCustom" value="#6d28d9" | |
| 157 | style="position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer;border:none;padding:0;background:transparent"> | |
| 158 | </div> | |
| 159 | </div> | |
| 160 | <!-- One hidden field carries the actual value either swatch or | |
| 161 | picker put into it. Keeps the server-side handler simple. --> | |
| 162 | <input type="hidden" name="color" id="catColor" value="#6d28d9"> | |
| 163 | </div> | |
| 164 | ||
| 165 | <!-- Emoji + picker takes a full row (the picker is wide; cramming | |
| 166 | it into a 2-col grid wastes ~half the modal). Sort order + | |
| 167 | Show-on-storefront move below into their own row. --> | |
| 168 | <div> | |
| 169 | <div class="form-group"> | |
| 170 | <label class="form-label">Emoji <span class="text-xs text-dim">(optional)</span></label> | |
| 171 | <input type="text" name="icon" id="catIcon" class="input" maxlength="40" placeholder="🎃"> | |
| 172 | <div class="cat-emoji-picker" id="catEmojiPicker"> | |
| 173 | <!-- ~100 emojis grouped by theme. Click inserts into the | |
| 174 | field; the X at the end clears. Buttons not links so | |
| 175 | they don't submit the form. --> | |
| 176 | <button type="button" class="cat-emoji-pick">📦</button><!-- box --> | |
| 177 | <button type="button" class="cat-emoji-pick">🎁</button><!-- gift --> | |
| 178 | <button type="button" class="cat-emoji-pick">💎</button><!-- gem --> | |
| 179 | <button type="button" class="cat-emoji-pick">🏷️</button><!-- label --> | |
| 180 | <button type="button" class="cat-emoji-pick">🔖</button><!-- bookmark --> | |
| 181 | <button type="button" class="cat-emoji-pick">📌</button><!-- pushpin --> | |
| 182 | <button type="button" class="cat-emoji-pick">🎯</button><!-- target --> | |
| 183 | <button type="button" class="cat-emoji-pick">🪙</button><!-- coin --> | |
| 184 | <button type="button" class="cat-emoji-pick">🎟️</button><!-- ticket --> | |
| 185 | <button type="button" class="cat-emoji-pick">🛍️</button><!-- shopping bag --> | |
| 186 | ||
| 187 | <button type="button" class="cat-emoji-pick">⭐</button><!-- star --> | |
| 188 | <button type="button" class="cat-emoji-pick">✨</button><!-- sparkles --> | |
| 189 | <button type="button" class="cat-emoji-pick">🌟</button><!-- glowing star --> | |
| 190 | <button type="button" class="cat-emoji-pick">💫</button><!-- dizzy --> | |
| 191 | <button type="button" class="cat-emoji-pick">⚡</button><!-- lightning --> | |
| 192 | <button type="button" class="cat-emoji-pick">🔥</button><!-- fire --> | |
| 193 | <button type="button" class="cat-emoji-pick">💧</button><!-- droplet --> | |
| 194 | <button type="button" class="cat-emoji-pick">❄️</button><!-- snowflake --> | |
| 195 | <button type="button" class="cat-emoji-pick">☀️</button><!-- sun --> | |
| 196 | <button type="button" class="cat-emoji-pick">🌈</button><!-- rainbow --> | |
| 197 | ||
| 198 | <button type="button" class="cat-emoji-pick">💖</button><!-- sparkling heart --> | |
| 199 | <button type="button" class="cat-emoji-pick">💝</button><!-- heart with ribbon --> | |
| 200 | <button type="button" class="cat-emoji-pick">❤️</button><!-- red heart --> | |
| 201 | <button type="button" class="cat-emoji-pick">🧡</button><!-- orange heart --> | |
| 202 | <button type="button" class="cat-emoji-pick">💛</button><!-- yellow heart --> | |
| 203 | <button type="button" class="cat-emoji-pick">💚</button><!-- green heart --> | |
| 204 | <button type="button" class="cat-emoji-pick">💙</button><!-- blue heart --> | |
| 205 | <button type="button" class="cat-emoji-pick">💜</button><!-- purple heart --> | |
| 206 | ||
| 207 | <button type="button" class="cat-emoji-pick">🎄</button><!-- christmas tree --> | |
| 208 | <button type="button" class="cat-emoji-pick">🎃</button><!-- jack-o-lantern --> | |
| 209 | <button type="button" class="cat-emoji-pick">🎆</button><!-- fireworks --> | |
| 210 | <button type="button" class="cat-emoji-pick">🎇</button><!-- sparkler --> | |
| 211 | <button type="button" class="cat-emoji-pick">🎈</button><!-- balloon --> | |
| 212 | <button type="button" class="cat-emoji-pick">🎊</button><!-- confetti ball --> | |
| 213 | <button type="button" class="cat-emoji-pick">🎉</button><!-- party popper --> | |
| 214 | <button type="button" class="cat-emoji-pick">🎀</button><!-- ribbon --> | |
| 215 | ||
| 216 | <button type="button" class="cat-emoji-pick">🐶</button><!-- dog --> | |
| 217 | <button type="button" class="cat-emoji-pick">🐱</button><!-- cat --> | |
| 218 | <button type="button" class="cat-emoji-pick">🦊</button><!-- fox --> | |
| 219 | <button type="button" class="cat-emoji-pick">🐰</button><!-- rabbit --> | |
| 220 | <button type="button" class="cat-emoji-pick">🐼</button><!-- panda --> | |
| 221 | <button type="button" class="cat-emoji-pick">🦁</button><!-- lion --> | |
| 222 | <button type="button" class="cat-emoji-pick">🐯</button><!-- tiger --> | |
| 223 | <button type="button" class="cat-emoji-pick">🐉</button><!-- dragon --> | |
| 224 | <button type="button" class="cat-emoji-pick">🦄</button><!-- unicorn --> | |
| 225 | <button type="button" class="cat-emoji-pick">🐢</button><!-- turtle --> | |
| 226 | <button type="button" class="cat-emoji-pick">🦋</button><!-- butterfly --> | |
| 227 | <button type="button" class="cat-emoji-pick">🐠</button><!-- tropical fish --> | |
| 228 | ||
| 229 | <button type="button" class="cat-emoji-pick">🌿</button><!-- herb --> | |
| 230 | <button type="button" class="cat-emoji-pick">🌱</button><!-- seedling --> | |
| 231 | <button type="button" class="cat-emoji-pick">🌳</button><!-- deciduous tree --> | |
| 232 | <button type="button" class="cat-emoji-pick">🌷</button><!-- tulip --> | |
| 233 | <button type="button" class="cat-emoji-pick">🌹</button><!-- rose --> | |
| 234 | <button type="button" class="cat-emoji-pick">🌻</button><!-- sunflower --> | |
| 235 | <button type="button" class="cat-emoji-pick">🍀</button><!-- four-leaf clover --> | |
| 236 | <button type="button" class="cat-emoji-pick">🍁</button><!-- maple leaf --> | |
| 237 | ||
| 238 | <button type="button" class="cat-emoji-pick">🍕</button><!-- pizza --> | |
| 239 | <button type="button" class="cat-emoji-pick">🍰</button><!-- cake --> | |
| 240 | <button type="button" class="cat-emoji-pick">🍩</button><!-- donut --> | |
| 241 | <button type="button" class="cat-emoji-pick">🍪</button><!-- cookie --> | |
| 242 | <button type="button" class="cat-emoji-pick">☕</button><!-- coffee --> | |
| 243 | <button type="button" class="cat-emoji-pick">🍺</button><!-- beer --> | |
| 244 | ||
| 245 | <button type="button" class="cat-emoji-pick">💡</button><!-- light bulb --> | |
| 246 | <button type="button" class="cat-emoji-pick">🔧</button><!-- wrench --> | |
| 247 | <button type="button" class="cat-emoji-pick">🔨</button><!-- hammer --> | |
| 248 | <button type="button" class="cat-emoji-pick">⚙️</button><!-- gear --> | |
| 249 | <button type="button" class="cat-emoji-pick">🛠️</button><!-- hammer & wrench --> | |
| 250 | <button type="button" class="cat-emoji-pick">💻</button><!-- laptop --> | |
| 251 | <button type="button" class="cat-emoji-pick">📱</button><!-- mobile phone --> | |
| 252 | <button type="button" class="cat-emoji-pick">⌨️</button><!-- keyboard --> | |
| 253 | <button type="button" class="cat-emoji-pick">🔌</button><!-- plug --> | |
| 254 | <button type="button" class="cat-emoji-pick">🪛</button><!-- screwdriver --> | |
| 255 | ||
| 256 | <button type="button" class="cat-emoji-pick">🎮</button><!-- video game --> | |
| 257 | <button type="button" class="cat-emoji-pick">🕹️</button><!-- joystick --> | |
| 258 | <button type="button" class="cat-emoji-pick">🎲</button><!-- die --> | |
| 259 | <button type="button" class="cat-emoji-pick">🧩</button><!-- puzzle piece --> | |
| 260 | <button type="button" class="cat-emoji-pick">🧸</button><!-- teddy bear --> | |
| 261 | <button type="button" class="cat-emoji-pick">🎨</button><!-- art palette --> | |
| 262 | <button type="button" class="cat-emoji-pick">🎭</button><!-- performing arts --> | |
| 263 | <button type="button" class="cat-emoji-pick">🪀</button><!-- yo-yo --> | |
| 264 | ||
| 265 | <button type="button" class="cat-emoji-pick">🏠</button><!-- house --> | |
| 266 | <button type="button" class="cat-emoji-pick">🛋️</button><!-- couch --> | |
| 267 | <button type="button" class="cat-emoji-pick">🛏️</button><!-- bed --> | |
| 268 | <button type="button" class="cat-emoji-pick">🪑</button><!-- chair --> | |
| 269 | <button type="button" class="cat-emoji-pick">🚪</button><!-- door --> | |
| 270 | <button type="button" class="cat-emoji-pick">🔑</button><!-- key --> | |
| 271 | ||
| 272 | <button type="button" class="cat-emoji-pick">🚗</button><!-- car --> | |
| 273 | <button type="button" class="cat-emoji-pick">🚲</button><!-- bicycle --> | |
| 274 | <button type="button" class="cat-emoji-pick">✈️</button><!-- airplane --> | |
| 275 | <button type="button" class="cat-emoji-pick">🚀</button><!-- rocket --> | |
| 276 | <button type="button" class="cat-emoji-pick">⛵</button><!-- sailboat --> | |
| 277 | <button type="button" class="cat-emoji-pick">🚂</button><!-- train --> | |
| 278 | ||
| 279 | <button type="button" class="cat-emoji-pick">⚽</button><!-- soccer --> | |
| 280 | <button type="button" class="cat-emoji-pick">🏀</button><!-- basketball --> | |
| 281 | <button type="button" class="cat-emoji-pick">🎾</button><!-- tennis --> | |
| 282 | <button type="button" class="cat-emoji-pick">🏋️</button><!-- weight lifter --> | |
| 283 | <button type="button" class="cat-emoji-pick">⛷️</button><!-- skier --> | |
| 284 | <button type="button" class="cat-emoji-pick">🏊</button><!-- swimmer --> | |
| 285 | ||
| 286 | <button type="button" class="cat-emoji-pick">👑</button><!-- crown --> | |
| 287 | <button type="button" class="cat-emoji-pick">🎩</button><!-- top hat --> | |
| 288 | <button type="button" class="cat-emoji-pick">👻</button><!-- ghost --> | |
| 289 | <button type="button" class="cat-emoji-pick">🤖</button><!-- robot --> | |
| 290 | <button type="button" class="cat-emoji-pick">💀</button><!-- skull --> | |
| 291 | <button type="button" class="cat-emoji-pick">🦇</button><!-- bat --> | |
| 292 | <button type="button" class="cat-emoji-pick">🧙</button><!-- mage --> | |
| 293 | <button type="button" class="cat-emoji-pick">🧚</button><!-- fairy --> | |
| 294 | ||
| 295 | <!-- ============== Second 100 emojis ============== --> | |
| 296 | ||
| 297 | <!-- Weather + sky --> | |
| 298 | <button type="button" class="cat-emoji-pick">🌤️</button><!-- sun behind small cloud --> | |
| 299 | <button type="button" class="cat-emoji-pick">⛅</button><!-- sun behind cloud --> | |
| 300 | <button type="button" class="cat-emoji-pick">☁️</button><!-- cloud --> | |
| 301 | <button type="button" class="cat-emoji-pick">🌧️</button><!-- rain --> | |
| 302 | <button type="button" class="cat-emoji-pick">⛈️</button><!-- thunderstorm --> | |
| 303 | <button type="button" class="cat-emoji-pick">🌪️</button><!-- tornado --> | |
| 304 | <button type="button" class="cat-emoji-pick">🌊</button><!-- ocean wave --> | |
| 305 | <button type="button" class="cat-emoji-pick">🌫️</button><!-- fog --> | |
| 306 | <button type="button" class="cat-emoji-pick">🌙</button><!-- crescent moon --> | |
| 307 | <button type="button" class="cat-emoji-pick">🌚</button><!-- new moon face --> | |
| 308 | <button type="button" class="cat-emoji-pick">🌝</button><!-- full moon face --> | |
| 309 | <button type="button" class="cat-emoji-pick">🌍</button><!-- earth europe --> | |
| 310 | ||
| 311 | <!-- More animals --> | |
| 312 | <button type="button" class="cat-emoji-pick">🐎</button><!-- horse --> | |
| 313 | <button type="button" class="cat-emoji-pick">🐄</button><!-- cow --> | |
| 314 | <button type="button" class="cat-emoji-pick">🐑</button><!-- sheep --> | |
| 315 | <button type="button" class="cat-emoji-pick">🐖</button><!-- pig --> | |
| 316 | <button type="button" class="cat-emoji-pick">🐔</button><!-- chicken --> | |
| 317 | <button type="button" class="cat-emoji-pick">🦆</button><!-- duck --> | |
| 318 | <button type="button" class="cat-emoji-pick">🦅</button><!-- eagle --> | |
| 319 | <button type="button" class="cat-emoji-pick">🦉</button><!-- owl --> | |
| 320 | <button type="button" class="cat-emoji-pick">🦜</button><!-- parrot --> | |
| 321 | <button type="button" class="cat-emoji-pick">🐝</button><!-- bee --> | |
| 322 | <button type="button" class="cat-emoji-pick">🐞</button><!-- ladybug --> | |
| 323 | <button type="button" class="cat-emoji-pick">🕷️</button><!-- spider --> | |
| 324 | <button type="button" class="cat-emoji-pick">🐙</button><!-- octopus --> | |
| 325 | <button type="button" class="cat-emoji-pick">🦀</button><!-- crab --> | |
| 326 | <button type="button" class="cat-emoji-pick">🦞</button><!-- lobster --> | |
| 327 | ||
| 328 | <!-- Fruits + vegetables --> | |
| 329 | <button type="button" class="cat-emoji-pick">🍎</button><!-- apple --> | |
| 330 | <button type="button" class="cat-emoji-pick">🍊</button><!-- orange --> | |
| 331 | <button type="button" class="cat-emoji-pick">🍋</button><!-- lemon --> | |
| 332 | <button type="button" class="cat-emoji-pick">🍇</button><!-- grapes --> | |
| 333 | <button type="button" class="cat-emoji-pick">🍓</button><!-- strawberry --> | |
| 334 | <button type="button" class="cat-emoji-pick">🥝</button><!-- kiwi --> | |
| 335 | <button type="button" class="cat-emoji-pick">🍑</button><!-- peach --> | |
| 336 | <button type="button" class="cat-emoji-pick">🥑</button><!-- avocado --> | |
| 337 | <button type="button" class="cat-emoji-pick">🥕</button><!-- carrot --> | |
| 338 | <button type="button" class="cat-emoji-pick">🌽</button><!-- corn --> | |
| 339 | <button type="button" class="cat-emoji-pick">🥖</button><!-- bread --> | |
| 340 | <button type="button" class="cat-emoji-pick">🍞</button><!-- baguette --> | |
| 341 | ||
| 342 | <!-- Drinks --> | |
| 343 | <button type="button" class="cat-emoji-pick">🍷</button><!-- wine glass --> | |
| 344 | <button type="button" class="cat-emoji-pick">🍸</button><!-- cocktail --> | |
| 345 | <button type="button" class="cat-emoji-pick">🥤</button><!-- cup with straw --> | |
| 346 | <button type="button" class="cat-emoji-pick">🧋</button><!-- bubble tea --> | |
| 347 | <button type="button" class="cat-emoji-pick">🥛</button><!-- glass of milk --> | |
| 348 | <button type="button" class="cat-emoji-pick">🍵</button><!-- teacup --> | |
| 349 | ||
| 350 | <!-- Music --> | |
| 351 | <button type="button" class="cat-emoji-pick">🎵</button><!-- musical note --> | |
| 352 | <button type="button" class="cat-emoji-pick">🎶</button><!-- musical notes --> | |
| 353 | <button type="button" class="cat-emoji-pick">🎤</button><!-- microphone --> | |
| 354 | <button type="button" class="cat-emoji-pick">🎧</button><!-- headphone --> | |
| 355 | <button type="button" class="cat-emoji-pick">🎸</button><!-- guitar --> | |
| 356 | <button type="button" class="cat-emoji-pick">🎹</button><!-- piano --> | |
| 357 | <button type="button" class="cat-emoji-pick">🥁</button><!-- drum --> | |
| 358 | <button type="button" class="cat-emoji-pick">🎺</button><!-- trumpet --> | |
| 359 | ||
| 360 | <!-- More sports + awards --> | |
| 361 | <button type="button" class="cat-emoji-pick">🏐</button><!-- volleyball --> | |
| 362 | <button type="button" class="cat-emoji-pick">🏈</button><!-- football --> | |
| 363 | <button type="button" class="cat-emoji-pick">⚾</button><!-- baseball --> | |
| 364 | <button type="button" class="cat-emoji-pick">🥊</button><!-- boxing glove --> | |
| 365 | <button type="button" class="cat-emoji-pick">🏆</button><!-- trophy --> | |
| 366 | <button type="button" class="cat-emoji-pick">🥇</button><!-- 1st place --> | |
| 367 | <button type="button" class="cat-emoji-pick">🏅</button><!-- medal --> | |
| 368 | <button type="button" class="cat-emoji-pick">🎖️</button><!-- military medal --> | |
| 369 | ||
| 370 | <!-- Science --> | |
| 371 | <button type="button" class="cat-emoji-pick">🔬</button><!-- microscope --> | |
| 372 | <button type="button" class="cat-emoji-pick">🔭</button><!-- telescope --> | |
| 373 | <button type="button" class="cat-emoji-pick">🧪</button><!-- test tube --> | |
| 374 | <button type="button" class="cat-emoji-pick">🧬</button><!-- DNA --> | |
| 375 | <button type="button" class="cat-emoji-pick">⚗️</button><!-- alembic --> | |
| 376 | <button type="button" class="cat-emoji-pick">🧲</button><!-- magnet --> | |
| 377 | <button type="button" class="cat-emoji-pick">🪐</button><!-- ringed planet --> | |
| 378 | <button type="button" class="cat-emoji-pick">☄️</button><!-- comet --> | |
| 379 | ||
| 380 | <!-- Travel + places --> | |
| 381 | <button type="button" class="cat-emoji-pick">🗺️</button><!-- world map --> | |
| 382 | <button type="button" class="cat-emoji-pick">🧭</button><!-- compass --> | |
| 383 | <button type="button" class="cat-emoji-pick">🌎</button><!-- earth americas --> | |
| 384 | <button type="button" class="cat-emoji-pick">🗽</button><!-- statue of liberty --> | |
| 385 | <button type="button" class="cat-emoji-pick">🗼</button><!-- tokyo tower --> | |
| 386 | <button type="button" class="cat-emoji-pick">🏔️</button><!-- snow-capped mountain --> | |
| 387 | <button type="button" class="cat-emoji-pick">🏖️</button><!-- beach --> | |
| 388 | <button type="button" class="cat-emoji-pick">🏝️</button><!-- desert island --> | |
| 389 | ||
| 390 | <!-- Office + school --> | |
| 391 | <button type="button" class="cat-emoji-pick">📚</button><!-- books --> | |
| 392 | <button type="button" class="cat-emoji-pick">📖</button><!-- open book --> | |
| 393 | <button type="button" class="cat-emoji-pick">📝</button><!-- memo --> | |
| 394 | <button type="button" class="cat-emoji-pick">✏️</button><!-- pencil --> | |
| 395 | <button type="button" class="cat-emoji-pick">📐</button><!-- triangular ruler --> | |
| 396 | <button type="button" class="cat-emoji-pick">📏</button><!-- straight ruler --> | |
| 397 | <button type="button" class="cat-emoji-pick">📎</button><!-- paperclip --> | |
| 398 | <button type="button" class="cat-emoji-pick">📋</button><!-- clipboard --> | |
| 399 | ||
| 400 | <!-- Money --> | |
| 401 | <button type="button" class="cat-emoji-pick">💰</button><!-- money bag --> | |
| 402 | <button type="button" class="cat-emoji-pick">💵</button><!-- dollar banknote --> | |
| 403 | <button type="button" class="cat-emoji-pick">💳</button><!-- credit card --> | |
| 404 | <button type="button" class="cat-emoji-pick">🧾</button><!-- receipt --> | |
| 405 | ||
| 406 | <!-- Time --> | |
| 407 | <button type="button" class="cat-emoji-pick">⏰</button><!-- alarm clock --> | |
| 408 | <button type="button" class="cat-emoji-pick">⏱️</button><!-- stopwatch --> | |
| 409 | <button type="button" class="cat-emoji-pick">⌛</button><!-- hourglass --> | |
| 410 | <button type="button" class="cat-emoji-pick">📅</button><!-- calendar --> | |
| 411 | ||
| 412 | <!-- More tools --> | |
| 413 | <button type="button" class="cat-emoji-pick">🪚</button><!-- saw --> | |
| 414 | <button type="button" class="cat-emoji-pick">🪟</button><!-- window --> | |
| 415 | <button type="button" class="cat-emoji-pick">🪤</button><!-- mouse trap --> | |
| 416 | <button type="button" class="cat-emoji-pick">🪣</button><!-- bucket --> | |
| 417 | <button type="button" class="cat-emoji-pick">🧹</button><!-- broom --> | |
| 418 | <button type="button" class="cat-emoji-pick">🪞</button><!-- mirror --> | |
| 419 | ||
| 420 | <!-- Marks + symbols --> | |
| 421 | <button type="button" class="cat-emoji-pick">✅</button><!-- check mark --> | |
| 422 | <button type="button" class="cat-emoji-pick">❌</button><!-- cross mark --> | |
| 423 | <button type="button" class="cat-emoji-pick">❗</button><!-- exclamation --> | |
| 424 | <button type="button" class="cat-emoji-pick">❓</button><!-- question --> | |
| 425 | <button type="button" class="cat-emoji-pick">➕</button><!-- plus --> | |
| 426 | ||
| 427 | <button type="button" class="cat-emoji-pick" title="Clear" data-clear="1">✕</button> | |
| 428 | </div> | |
| 429 | <div class="text-xs text-dim" style="margin-top:4px;line-height:1.4">Click an emoji above (scroll for more), or type your own. Win+. on Windows / Ctrl+Cmd+Space on Mac opens the OS emoji picker.</div> | |
| 430 | </div> | |
| 431 | </div> | |
| 432 | ||
| 433 | <div class="dash-grid-2" style="gap:10px;align-items:end"> | |
| 434 | <div class="form-group"> | |
| 435 | <label class="form-label">Sort order</label> | |
| 436 | <input type="number" name="sort_order" id="catSort" class="input" value="0" min="0"> | |
| 437 | </div> | |
| 438 | <div class="form-group"> | |
| 439 | <label class="up-check" style="display:flex;align-items:center;gap:8px;font-size:13px;color:var(--col-text-2);cursor:pointer;padding:9px 0"> | |
| 440 | <input type="checkbox" name="is_public" id="catPublic" value="1" checked> | |
| 441 | <span>Show on storefront filter bar</span> | |
| 442 | </label> | |
| 443 | </div> | |
| 444 | </div> | |
| 445 | ||
| 446 | <div style="display:flex;gap:8px;justify-content:flex-end;margin-top:8px"> | |
| 447 | <button type="button" class="btn btn-secondary" id="catCancel">Cancel</button> | |
| 448 | <button type="submit" class="btn btn-primary">Save category</button> | |
| 449 | </div> | |
| 450 | </form> | |
| 451 | </div> | |
| 452 | </div> | |
| 453 | ||
| 454 | <script> | |
| 455 | (function(){ | |
| 456 | var modal = document.getElementById('catModal'); | |
| 457 | var titleEl = document.getElementById('catModalTitle'); | |
| 458 | var form = document.getElementById('catForm'); | |
| 459 | var actInput = document.getElementById('catAct'); | |
| 460 | var idInput = document.getElementById('catId'); | |
| 461 | var nameInput = document.getElementById('catName'); | |
| 462 | var descInput = document.getElementById('catDesc'); | |
| 463 | var iconInput = document.getElementById('catIcon'); | |
| 464 | var sortInput = document.getElementById('catSort'); | |
| 465 | var publicInput = document.getElementById('catPublic'); | |
| 466 | var colorHidden = document.getElementById('catColor'); | |
| 467 | var colorPicker = document.getElementById('catColorCustom'); | |
| 468 | var customWrap = document.querySelector('.cat-color-custom-wrap'); | |
| 469 | var customDot = document.getElementById('catCustomDot'); | |
| 470 | var swatches = document.querySelectorAll('#catColors input[type=radio]'); | |
| 471 | ||
| 472 | function applySwatch(value) { | |
| 473 | colorHidden.value = value; | |
| 474 | customDot.style.display = 'none'; | |
| 475 | customWrap.classList.remove('is-active'); | |
| 476 | } | |
| 477 | function applyCustom(value) { | |
| 478 | colorHidden.value = value; | |
| 479 | swatches.forEach(function(r){ r.checked = false; }); | |
| 480 | customDot.style.background = value; | |
| 481 | customDot.style.display = 'block'; | |
| 482 | customWrap.classList.add('is-active'); | |
| 483 | } | |
| 484 | ||
| 485 | swatches.forEach(function(r){ | |
| 486 | r.addEventListener('change', function(){ if (r.checked) applySwatch(r.value); }); | |
| 487 | }); | |
| 488 | // 'input' fires while dragging; 'change' fires when the dialog | |
| 489 | // closes. Listen to both so the chip preview tracks the picker live | |
| 490 | // and the final value is committed even if the user clicks away | |
| 491 | // without releasing on a color. | |
| 492 | colorPicker.addEventListener('input', function(){ applyCustom(colorPicker.value); }); | |
| 493 | colorPicker.addEventListener('change', function(){ applyCustom(colorPicker.value); }); | |
| 494 | ||
| 495 | function setColorFromValue(c) { | |
| 496 | var matched = false; | |
| 497 | swatches.forEach(function(r){ | |
| 498 | if (r.value.toLowerCase() === c.toLowerCase()) { r.checked = true; matched = true; } | |
| 499 | else { r.checked = false; } | |
| 500 | }); | |
| 501 | if (matched) { applySwatch(c); } | |
| 502 | else { applyCustom(c); colorPicker.value = c; } | |
| 503 | } | |
| 504 | ||
| 505 | // Emoji quick-pick: insert clicked emoji into the icon field. | |
| 506 | document.querySelectorAll('.cat-emoji-pick').forEach(function(b){ | |
| 507 | b.addEventListener('click', function(){ | |
| 508 | if (b.dataset.clear) { iconInput.value = ''; } | |
| 509 | else { iconInput.value = b.textContent.trim(); } | |
| 510 | }); | |
| 511 | }); | |
| 512 | ||
| 513 | function openNew() { | |
| 514 | titleEl.textContent = 'New category'; | |
| 515 | actInput.value = 'create'; | |
| 516 | idInput.value = ''; | |
| 517 | nameInput.value = ''; | |
| 518 | descInput.value = ''; | |
| 519 | iconInput.value = ''; | |
| 520 | sortInput.value = '0'; | |
| 521 | publicInput.checked = true; | |
| 522 | setColorFromValue('#6d28d9'); | |
| 523 | modal.classList.add('is-open'); | |
| 524 | setTimeout(function(){ nameInput.focus(); }, 50); | |
| 525 | } | |
| 526 | function openEdit(btn) { | |
| 527 | titleEl.textContent = 'Edit category'; | |
| 528 | actInput.value = 'update'; | |
| 529 | idInput.value = btn.dataset.id; | |
| 530 | nameInput.value = btn.dataset.name || ''; | |
| 531 | descInput.value = btn.dataset.desc || ''; | |
| 532 | iconInput.value = btn.dataset.icon || ''; | |
| 533 | sortInput.value = btn.dataset.sort || '0'; | |
| 534 | publicInput.checked = btn.dataset.public === '1'; | |
| 535 | setColorFromValue(btn.dataset.color || '#6d28d9'); | |
| 536 | modal.classList.add('is-open'); | |
| 537 | } | |
| 538 | function close() { modal.classList.remove('is-open'); } | |
| 539 | var newBtn = document.getElementById('catOpenNew'); | |
| 540 | if (newBtn) newBtn.addEventListener('click', openNew); | |
| 541 | var newBtnEmpty = document.getElementById('catOpenNewEmpty'); | |
| 542 | if (newBtnEmpty) newBtnEmpty.addEventListener('click', openNew); | |
| 543 | document.querySelectorAll('.cat-edit').forEach(function(b){ | |
| 544 | b.addEventListener('click', function(){ openEdit(b); }); | |
| 545 | }); | |
| 546 | document.getElementById('catCancel').addEventListener('click', close); | |
| 547 | modal.addEventListener('click', function(e){ if (e.target === modal) close(); }); | |
| 548 | document.addEventListener('keydown', function(e){ if (e.key === 'Escape') close(); }); | |
| 549 | })(); | |
| 550 | </script> |