1import { getCmsApiBaseUrl } from './cms-config.js' 2 3const AREA_ORDER = [ 4 '颿±ã¨ãªã¢', 5 'åæµ·éã»æ±åã¨ãªã¢', 6 'åä¿¡è¶ã»æ±æµ·ã¨ãªã¢', 7 'é¢è¥¿ã¨ãªã¢', 8 'ä¸å½ã»åå½ã¨ãªã¢', 9 'ä¹å·ã»æ²ç¸ã¨ãªã¢', 10] 11 12const PREFECTURE_ORDER = [ 13 'æ±äº¬é½', 14 'ç¥å¥å·ç', 15 'å¼çç', 16 'åèç', 17 'è¨åç', 18 'æ æ¨ç', 19 '群馬ç', 20 'åæµ·é', 21 '鿣®ç', 22 '岩æç', 23 'å®®åç', 24 'ç§ç°ç', 25 '山形ç', 26 'ç¦å³¶ç', 27 'æ°æ½ç', 28 'å¯å±±ç', 29 'ç³å·ç', 30 'ç¦äºç', 31 '山梨ç', 32 'é·éç', 33 'å²éç', 34 'é岡ç', 35 'æç¥ç', 36 'ä¸éç', 37 'æ»è³ç', 38 '京é½åº', 39 '大éªåº', 40 'å µåº«ç', 41 'å¥è¯ç', 42 'åæå±±ç', 43 'é³¥åç', 44 'å³¶æ ¹ç', 45 '岡山ç', 46 'åºå³¶ç', 47 'å±±å£ç', 48 '徳島ç', 49 'é¦å·ç', 50 'æåªç', 51 'é«ç¥ç', 52 'ç¦å²¡ç', 53 'ä½è³ç', 54 'é·å´ç', 55 'çæ¬ç', 56 '大åç', 57 'å®®å´ç', 58 '鹿å å³¶ç', 59 'æ²ç¸ç', 60] 61 62const SHOP_CATEGORY_SLUGS = ['boutique', 'pharmacy', 'inside-sugi'] 63 64const normalizePath = () => window.location.pathname.replace(/\/index\.html$/, '/') 65 66const isShopIndexPage = () => normalizePath().endsWith('/shop/') 67 68const shopCategorySlug = () => { 69 const parts = normalizePath().split('/').filter(Boolean) 70 if (parts.length === 2 && parts[0] === 'shop' && SHOP_CATEGORY_SLUGS.includes(parts[1])) { 71 return parts[1] 72 } 73 return '' 74} 75 76const isShopCategoryPage = () => Boolean(shopCategorySlug()) 77 78const isShopDetailPage = () => document.body.classList.contains('page-shop-detail') 79 80const assetBase = () => { 81 if (document.body.dataset.assetBase) { 82 return document.body.dataset.assetBase.replace(/\/$/, '') 83 } 84 85 const parts = normalizePath().split('/').filter(Boolean) 86 return parts.length ? Array(parts.length).fill('..').join('/') : '.' 87} 88 89const apiBase = () => (getCmsApiBaseUrl() || document.body.dataset.cmsApiUrl || '').replace(/\/$/, '') 90 91const assetUrl = (path) => { 92 if (!path) return '' 93 if (/^https?:\/\//.test(path)) return path 94 return `${assetBase()}/${String(path).replace(/^\/+/, '')}` 95} 96 97const currentShopSlug = () => { 98 if (document.body.dataset.shopSlug) return document.body.dataset.shopSlug 99 const parts = normalizePath().split('/').filter(Boolean) 100 return parts[parts.length - 1] || '' 101} 102 103const fetchStores = async () => { 104 const response = await fetch(`${apiBase()}/shop-stores`, { headers: { Accept: 'application/json' } }) 105 if (!response.ok) throw new Error(`shop stores api error: ${response.status}`) 106 return response.json() 107} 108 109const fetchStore = async (slug) => { 110 const response = await fetch(`${apiBase()}/shop-stores/${encodeURIComponent(slug)}`, { headers: { Accept: 'application/json' } }) 111 if (!response.ok) throw new Error(`shop store api error: ${response.status}`) 112 return response.json() 113} 114 115const setShopIndexLoading = (isLoading) => { 116 if (!isShopIndexPage()) return 117 118 document.querySelectorAll('.area-nav__groups, .l-section').forEach((element) => { 119 if (element.querySelector('.store-area') || element.classList.contains('area-nav__groups')) { 120 element.style.visibility = isLoading ? 'hidden' : '' 121 } 122 }) 123} 124 125const setShopCategoryLoading = (isLoading) => { 126 if (!isShopCategoryPage()) return 127 128 document.querySelectorAll('.l-section, .l-stack').forEach((element) => { 129 if (element.querySelector('.store-area, .store-card')) { 130 element.style.visibility = isLoading ? 'hidden' : '' 131 } 132 }) 133} 134 135const setShopDetailLoading = (isLoading) => { 136 if (!isShopDetailPage()) return 137 138 document.querySelectorAll('.shop-detail__head, .shop-detail__hero, .shop-detail > .shop-detail__section').forEach((element) => { 139 element.style.visibility = isLoading ? 'hidden' : '' 140 }) 141} 142 143const orderIndex = (list, value) => { 144 const index = list.indexOf(value) 145 return index === -1 ? list.length : index 146} 147 148const sortAreas = (areas) => [...areas].sort((a, b) => orderIndex(AREA_ORDER, a) - orderIndex(AREA_ORDER, b) || a.localeCompare(b, 'ja')) 149 150const sortPrefectures = (prefectures) => [...prefectures].sort((a, b) => orderIndex(PREFECTURE_ORDER, a) - orderIndex(PREFECTURE_ORDER, b) || a.localeCompare(b, 'ja')) 151 152const textWithBreaks = (element, value) => {
153 String(value || '').split(/\r?\n/).forEach((line, index) => { 154 if (index > 0) element.appendChild(document.createElement('br')) 155 element.appendChild(document.createTextNode(line)) 156 }) 157} 158 159const icon = (name) => { 160 const element = document.createElement('span') 161 element.className = `c-icon c-icon--${name}` 162 element.setAttribute('aria-hidden', 'true') 163 return element 164} 165 166const appendLinkText = (link, text, iconName) => { 167 const label = document.createElement('span') 168 label.className = 'btn__text' 169 label.textContent = text 170 link.appendChild(label) 171 link.appendChild(icon(iconName)) 172} 173 174const createStoreCard = (store) => { 175 const item = document.createElement('li') 176 item.className = 'card-grid__item' 177 178 const article = document.createElement('article') 179 article.className = 'store-card' 180 181 const badge = document.createElement('span') 182 badge.className = `brand-badge ${store.brand_badge_class || 'brand-badge--nihondo'}` 183 badge.textContent = store.brand_label || '' 184 article.appendChild(badge) 185 186 const name = document.createElement('a') 187 name.className = 'store-card__name' 188 name.href = store.url || '#' 189 name.textContent = store.name || '' 190 if (store.detail_type === 'external') { 191 name.target = '_blank' 192 name.rel = 'noopener noreferrer' 193 } 194 article.appendChild(name) 195 196 const info = document.createElement('dl') 197 info.className = 'store-card__info' 198 ;[ 199 ['使', [store.postal_code, store.address].filter(Boolean).join(' ')], 200 ['ã¢ã¯ã»ã¹', store.access_summary], 201 ['é»è©±çªå·', store.tel], 202 ['å¶æ¥æé', store.business_hours], 203 ['å®ä¼æ¥', store.holiday], 204 ].forEach(([label, value]) => { 205 if (!value) return 206 const row = document.createElement('div') 207 row.className = 'store-card__row' 208 const dt = document.createElement('dt') 209 dt.textContent = label 210 const dd = document.createElement('dd') 211 textWithBreaks(dd, value) 212 row.appendChild(dt) 213 row.appendChild(dd) 214 info.appendChild(row) 215 }) 216 article.appendChild(info) 217 218 const tags = document.createElement('div') 219 tags.className = 'store-card__tags' 220 if (store.has_kampo_consultation) { 221 const tag = document.createElement('span') 222 tag.className = 'store-card__tag store-card__tag--soudan' 223 tag.textContent = 'æ¼¢æ¹ç¸è«' 224 tags.appendChild(tag) 225 } 226 if (store.has_product_sales) { 227 const tag = document.createElement('span') 228 tag.className = 'store-card__tag store-card__tag--bukken' 229 tag.textContent = 'ç©è²©' 230 tags.appendChild(tag) 231 } 232 if (store.is_tax_free) { 233 const tax = document.createElement('span') 234 tax.className = 'store-card__tax' 235 tax.textContent = 'TAX FREE' 236 tags.appendChild(tax) 237 } 238 article.appendChild(tags) 239 240 const detail = document.createElement('a') 241 detail.className = 'btn btn--outline-dark store-card__detail' 242 detail.href = store.url || '#' 243 if (store.detail_type === 'external') { 244 detail.target = '_blank' 245 detail.rel = 'noopener noreferrer' 246 } 247 appendLinkText(detail, 'åºè詳細ã¸', store.detail_type === 'external' ? 'north-east' : 'arrow-forward') 248 article.appendChild(detail) 249 250 item.appendChild(article) 251 return item 252} 253 254const renderShopIndex = (stores) => { 255 renderAreaNav(stores) 256 257 const firstStoreArea = document.querySelector('.store-area') 258 const firstSection = firstStoreArea?.closest('.l-section') 259 const parent = firstSection?.parentNode 260 if (!firstSection || !parent) return 261 262 parent.querySelectorAll('.l-section').forEach((section) => { 263 if (section.querySelector('.store-area')) section.remove() 264 }) 265 266 if (!stores.length) return 267 268 const areas = sortAreas([...new Set(stores.map((store) => store.area_label).filter(Boolean))]) 269 areas.forEach((area) => { 270 const areaSection = document.createElement('section') 271 areaSection.className = 'l-section' 272 const sectionHead = document.createElement('div') 273 sectionHead.className = 'section-head section-head--brown-700' 274 const h2 = document.createElement('h2') 275 h2.className = 'section-head__title' 276 h2.textContent = area 277 sectionHead.appendChild(h2) 278 areaSection.appendChild(sectionHead) 279 280 const areaStores = stores.filter((store) => store.area_label === area) 281 const prefectures = sortPrefectures([...new Set(areaStores.map((store) =>
281 store.prefecture).filter(Boolean))]) 282 prefectures.forEach((prefecture) => { 283 const storeArea = document.createElement('div') 284 storeArea.className = 'store-area' 285 const h3 = document.createElement('h3') 286 h3.className = 'region-head' 287 h3.id = areaStores.find((store) => store.prefecture === prefecture)?.prefecture_slug || '' 288 h3.textContent = prefecture 289 const grid = document.createElement('ul') 290 grid.className = 'card-grid' 291 areaStores.filter((store) => store.prefecture === prefecture).forEach((store) => grid.appendChild(createStoreCard(store))) 292 storeArea.appendChild(h3) 293 storeArea.appendChild(grid) 294 areaSection.appendChild(storeArea) 295 }) 296 297 parent.appendChild(areaSection) 298 }) 299} 300 301const appendStoreGrid = (parent, stores) => { 302 const grid = document.createElement('ul') 303 grid.className = 'card-grid' 304 stores.forEach((store) => grid.appendChild(createStoreCard(store))) 305 parent.appendChild(grid) 306} 307 308const appendPrefectureStoreGroups = (parent, stores) => { 309 const prefectures = sortPrefectures([...new Set(stores.map((store) => store.prefecture).filter(Boolean))]) 310 prefectures.forEach((prefecture) => { 311 const storeArea = document.createElement('div') 312 storeArea.className = 'store-area' 313 const h3 = document.createElement('h3') 314 h3.className = 'region-head' 315 h3.id = stores.find((store) => store.prefecture === prefecture)?.prefecture_slug || '' 316 h3.textContent = prefecture 317 const grid = document.createElement('ul') 318 grid.className = 'card-grid' 319 stores.filter((store) => store.prefecture === prefecture).forEach((store) => grid.appendChild(createStoreCard(store))) 320 storeArea.appendChild(h3) 321 storeArea.appendChild(grid) 322 parent.appendChild(storeArea) 323 }) 324} 325 326const createStoreListSection = (stores, sectionClassName = 'l-section', options = {}) => { 327 const section = document.createElement('section') 328 section.className = sectionClassName 329 330 const sectionHead = document.createElement('div') 331 sectionHead.className = sectionClassName === 'l-stack' 332 ? 'section-head section-head--brown' 333 : 'section-head section-head--brown-700' 334 const h2 = document.createElement('h2') 335 h2.className = 'section-head__title' 336 h2.textContent = 'åºèä¸è¦§' 337 sectionHead.appendChild(h2) 338 section.appendChild(sectionHead) 339 340 if (options.groupByPrefecture) { 341 appendPrefectureStoreGroups(section, stores) 342 } else { 343 appendStoreGrid(section, stores) 344 } 345 346 return section 347} 348 349const renderShopCategory = (stores) => { 350 const categorySlug = shopCategorySlug() 351 const categoryStores = stores.filter((store) => store.category_slug === categorySlug) 352 const firstStoreList = document.querySelector('.store-area, .store-card') 353 const firstSection = firstStoreList?.closest('.l-section, .l-stack') 354 const parent = firstSection?.parentNode 355 if (!firstSection || !parent) return 356 357 const sectionClassName = firstSection.classList.contains('l-stack') ? 'l-stack' : 'l-section' 358 parent.querySelectorAll('.l-section, .l-stack').forEach((section) => { 359 if (section.querySelector('.store-area, .store-card')) section.remove() 360 }) 361 362 if (!categoryStores.length) return 363 364 parent.appendChild(createStoreListSection(categoryStores, sectionClassName, {
365 groupByPrefecture: categorySlug !== 'pharmacy', 366 })) 367} 368 369const renderAreaNav = (stores) => { 370 const navGroups = document.querySelector('.area-nav__groups') 371 if (!navGroups) return 372 373 navGroups.replaceChildren() 374 if (!stores.length) return 375 376 const fragment = document.createDocumentFragment() 377 const areas = sortAreas([...new Set(stores.map((store) => store.area_label).filter(Boolean))]) 378 379 areas.forEach((area) => { 380 const group = document.createElement('div') 381 group.className = 'area-nav__group' 382 383 const label = document.createElement('p') 384 label.className = 'area-nav__label' 385 label.textContent = area.replace(/ã¨ãªã¢$/, '') 386 387 const links = document.createElement('div') 388 links.className = 'area-nav__links' 389 390 const areaStores = stores.filter((store) => store.area_label === area) 391 const prefectures = sortPrefectures([...new Map(areaStores 392 .filter((store) => store.prefecture && store.prefecture_slug) 393 .map((store) => [store.prefecture, store.prefecture_slug])).keys()]) 394 395 prefectures.forEach((prefecture) => { 396 const slug = areaStores.find((store) => store.prefecture === prefecture)?.prefecture_slug 397 if (!slug) return 398 const link = document.createElement('a') 399 link.className = 'area-nav__link' 400 link.href = `#${slug}` 401 link.appendChild(document.createTextNode(prefecture)) 402 link.appendChild(icon('keyboard-arrow-down')) 403 links.appendChild(link) 404 }) 405 406 group.appendChild(label) 407 group.appendChild(links) 408 fragment.appendChild(group) 409 }) 410 411 navGroups.replaceChildren(fragment) 412} 413 414const createInfoRow = (label, value, linkHref = '') => { 415 if (!value) return null 416 const row = document.createElement('div') 417 row.className = 'shop-detail__info-row' 418 const dt = document.createElement('dt') 419 dt.className = 'shop-detail__info-dt' 420 dt.textContent = label 421 const dd = document.createElement('dd') 422 dd.className = 'shop-detail__info-dd' 423 if (linkHref) { 424 const link = document.createElement('a') 425 link.href = linkHref 426 link.textContent = value 427 dd.appendChild(link) 428 } else { 429 textWithBreaks(dd, value) 430 } 431 row.appendChild(dt) 432 row.appendChild(dd) 433 return row 434} 435 436const shopDetailName = (store) => store.detail_name || store.name || '' 437 438const createSection = (heading, content) => { 439 const section = document.createElement('section') 440 section.className = 'shop-detail__section' 441 const h2 = document.createElement('h2') 442 h2.className = 'shop-detail__heading' 443 h2.textContent = heading 444 section.appendChild(h2) 445 section.appendChild(content) 446 return section 447} 448 449const renderHero = (store) => { 450 const hero = document.querySelector('[data-shop-hero]') || document.querySelector('.shop-detail__hero') 451 if (!hero) return 452 const images = store.hero_images || [] 453 hero.replaceChildren() 454 if (!images.length) return 455 456 if (images.length === 1) { 457 const image = document.createElement('img') 458 image.src = assetUrl(images[0].path) 459 image.alt = images[0].alt || shopDetailName(store) 460 image.width = 1200 461 image.height = 500 462 image.loading = 'lazy' 463 hero.appendChild(image) 464 return 465 } 466 467 const swiper = document.createElement('div') 468 swiper.className = 'shop-detail__hero-swiper swiper' 469 swiper.id = 'js-shop-hero-swiper' 470 const wrapper = document.createElement('div') 471 wrapper.className = 'swiper-wrapper' 472 images.forEach((heroImage) => { 473 const slide = document.createElement('div') 474 slide.className = 'swiper-slide' 475 const image = document.createElement('img') 476 image.src = assetUrl(heroImage.path) 477 image.alt = heroImage.alt || shopDetailName(store) 478 image.width = 1200 479 image.height = 500 480 image.loading = 'lazy' 481 slide.appendChild(image) 482 wrapper.appendChild(slide) 483 }) 484 swiper.appendChild(wrapper) 485 hero.appendChild(swiper) 486} 487 488const renderDetailSections = (store) => { 489 let mount = document.querySelector('[data-shop-sections]') 490 const shopDetail = document.querySelector('.shop-detail') 491 const insertBefore = document.querySelector('.shop-detail__form') || document.querySelector('.shop-detail__back') 492 493 if (!mount && shopDetail && insertBefore) { 494 document.querySelectorAll('.shop-detail > .shop-detail__section').forEach((section) => section.remove()) 495 mount = document.createElement('div') 496 mount.dataset.shopSections = '' 497 shopDetail.insertBefore(mount, insertBefore) 498 } 499 500 if (!mount) return 501 const fragment = document.createDocumentFragment() 502 503 const infoWrap = document.createElement('div') 504 infoWrap.className = 'shop-detail__info' 505 const dl = document.createElement('dl') 506 dl.className = 'shop-detail__info-dl' 507 ;[ 508 createInfoRow('使', [store.postal_code, store.address].filter(
508Boolean).join(' ')), 509 createInfoRow('TEL', store.tel, store.tel_link), 510 createInfoRow('ã¡ã¼ã«', store.email, store.email ? `mailto:${store.email}` : ''), 511 createInfoRow('å¶æ¥æé', store.business_hours), 512 createInfoRow('å®ä¼æ¥', store.holiday), 513 ].filter(Boolean).forEach((row) => dl.appendChild(row)) 514 ;(store.info_rows || []).forEach((row) => { 515 const infoRow = createInfoRow(row.label, row.value) 516 if (infoRow) dl.appendChild(infoRow) 517 }) 518 if (store.is_tax_free) { 519 const row = createInfoRow('Tax-Free', 'JAPAN TAX-FREE SHOP') 520 const dd = row.querySelector('dd') 521 if (dd) { 522 dd.replaceChildren() 523 const badge = document.createElement('span') 524 badge.className = 'shop-detail__taxfree' 525 badge.textContent = 'JAPAN TAX-FREE SHOP' 526 dd.appendChild(badge) 527 } 528 dl.appendChild(row) 529 } 530 infoWrap.appendChild(dl) 531 if (store.map_embed_url) { 532 const map = document.createElement('div') 533 map.className = 'shop-detail__map' 534 const iframe = document.createElement('iframe') 535 iframe.src = store.map_embed_url 536 iframe.title = `${shopDetailName(store)} å°å³` 537 iframe.allowFullscreen = true 538 iframe.loading = 'lazy' 539 iframe.referrerPolicy = 'no-referrer-when-downgrade' 540 map.appendChild(iframe) 541 infoWrap.appendChild(map) 542 } 543 fragment.appendChild(createSection('åºèæ å ±', infoWrap)) 544 545 if ((store.access_rows || []).length) { 546 const access = document.createElement('div') 547 access.className = 'shop-detail__access' 548 store.access_rows.forEach((text) => { 549 const item = document.createElement('div') 550 item.className = 'shop-detail__access-item' 551 const accessIcon = icon('train') 552 accessIcon.classList.add('shop-detail__access-icon') 553 const p = document.createElement('p') 554 p.className = 'shop-detail__access-text' 555 textWithBreaks(p, text) 556 item.appendChild(accessIcon) 557 item.appendChild(p) 558 access.appendChild(item) 559 }) 560 fragment.appendChild(createSection('ã¢ã¯ã»ã¹', access)) 561 } 562 563 if ((store.message_paragraphs || []).length) { 564 const message = document.createElement('div') 565 message.className = 'shop-detail__message' 566 store.message_paragraphs.forEach((text) => { 567 const p = document.createElement('p') 568 textWithBreaks(p, text) 569 message.appendChild(p) 570 }) 571 const section = createSection('åºèããã®ã¡ãã»ã¼ã¸', message) 572 const messageBanner = store.message_banner || {} 573 if (messageBanner.image_path) { 574 const banner = document.createElement('div') 575 banner.className = 'shop-detail__banner' 576 const image = document.createElement('img') 577 image.src = assetUrl(messageBanner.image_path) 578 image.alt = messageBanner.alt || '' 579 image.width = 345 580 image.height = 90 581 image.loading = 'lazy' 582 583 if (messageBanner.link_url) { 584 const link = document.createElement('a') 585 link.className = 'shop-detail__banner-link' 586 link.href = messageBanner.link_url 587 link.appendChild(image) 588 banner.appendChild(link) 589 } else { 590 banner.appendChild(image) 591 } 592 593 section.appendChild(banner) 594 } 595 fragment.appendChild(section) 596 } 597 598 if ((store.services || []).length) { 599 const services = document.createElement('div') 600 services.className = 'shop-detail__services' 601 store.services.forEach((service) => { 602 const item = document.createElement('div') 603 item.className = 'shop-detail__service' 604 const head = document.createElement('div') 605 head.className = 'shop-detail__service-head' 606 const serviceIcon = icon(service.icon || 'service-store') 607 serviceIcon.classList.add('shop-detail__service-icon') 608 const title = document.createElement('h3') 609 title.className = 'shop-detail__service-title' 610 title.textContent = service.title || '' 611 head.appendChild(serviceIcon) 612 head.appendChild(title) 613 const desc = document.createElement('p') 614 desc.className = 'shop-detail__service-desc' 615 desc.textContent = service.description || '' 616 item.appendChild(head) 617 item.appendChild(desc) 618 services.appendChild(item) 619 }) 620 fragment.appendChild(createSection('æä¾ãµã¼ãã¹', services)) 621 } 622 623 mount.replaceChildren(fragment) 624} 625 626const renderConsultTypes = (form, store) => { 627 const group = form.querySelector('input[name="consult_type"]')?.closest('.contact-form__radios') 628 if (!group || !(store.consult_types || []).length) return 629 group.replaceChildren()
630 store.consult_types.forEach((type) => { 631 const label = document.createElement('label') 632 label.className = 'contact-form__radio-label' 633 const input = document.createElement('input') 634 input.type = 'radio' 635 input.className = 'contact-form__radio' 636 input.name = 'consult_type' 637 input.value = type 638 input.dataset.validate = 'required' 639 input.dataset.label = 'ãç¸è«å½¢å¼' 640 const span = document.createElement('span') 641 span.textContent = type 642 label.appendChild(input) 643 label.appendChild(span) 644 group.appendChild(label) 645 }) 646} 647 648const renderReservation = (store) => { 649 const reserveLink = document.querySelector('[data-shop-reserve-link], .shop-detail__reserve') 650 if (reserveLink) reserveLink.hidden = !store.has_reservation_form 651 const existingForm = document.querySelector('form[data-storage-key="shop-reservation"]') 652 if (!store.has_reservation_form) { 653 existingForm?.closest('.shop-detail__form')?.remove() 654 return 655 } 656 if (!existingForm) return 657 658 const fields = { 659 shop_name: shopDetailName(store), 660 shop_tel: store.tel || '', 661 shop_email: store.email || '', 662 } 663 Object.entries(fields).forEach(([name, value]) => { 664 const input = existingForm.querySelector(`input[name="${name}"]`) 665 if (input) input.value = value 666 }) 667 renderConsultTypes(existingForm, store) 668} 669 670const renderShopDetail = (store) => { 671 const detailName = shopDetailName(store) 672 const title = document.querySelector('[data-shop-title], .shop-detail__title') 673 if (title) title.textContent = detailName 674 const breadcrumb = document.querySelector('[data-shop-breadcrumb-current]') 675 if (breadcrumb) breadcrumb.textContent = detailName 676 const lead = document.querySelector('[data-shop-lead], .shop-detail__sub') 677 if (lead) { 678 lead.textContent = store.lead_text || '' 679 lead.hidden = !store.lead_text 680 } 681 const brand = document.querySelector('[data-shop-brand], .shop-detail__badge') 682 if (brand) { 683 brand.className = `brand-badge ${store.brand_badge_class || 'brand-badge--nihondo'} shop-detail__badge` 684 brand.textContent = store.brand_label || '' 685 } 686 renderHero(store) 687 renderDetailSections(store) 688 renderReservation(store) 689} 690 691export async function cmsShopStores() { 692 setShopIndexLoading(true) 693 setShopCategoryLoading(true) 694 setShopDetailLoading(true) 695 696 try { 697 if (isShopIndexPage() || isShopCategoryPage()) { 698 const stores = await fetchStores() 699 if (Array.isArray(stores)) { 700 if (isShopIndexPage()) renderShopIndex(stores) 701 if (isShopCategoryPage()) renderShopCategory(stores) 702 } 703 } 704 705 if (isShopDetailPage()) { 706 const slug = currentShopSlug() 707 if (!slug) return 708 const store = await fetchStore(slug) 709 renderShopDetail(store) 710 } 711 } catch (error) { 712 console.warn(error) 713 } finally { 714 setShopIndexLoading(false) 715 setShopCategoryLoading(false) 716 setShopDetailLoading(false) 717 } 718}
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.