{"id":10999,"date":"2026-03-14T15:50:25","date_gmt":"2026-03-14T15:50:25","guid":{"rendered":"https:\/\/walin.jp\/kimonovoice\/?p=10999"},"modified":"2026-03-14T15:59:29","modified_gmt":"2026-03-14T15:59:29","slug":"10999-2","status":"publish","type":"post","link":"https:\/\/walin.jp\/kimonovoice\/10999-2\/","title":{"rendered":""},"content":{"rendered":"\n<div id=\"wln-search-tool-wrapper\">\n    <!-- Google Fonts -->\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Noto+Serif+JP:wght@400;500;700&#038;family=Montserrat:wght@400;600&#038;display=swap\" rel=\"stylesheet\">\n\n    <style>\n        #wln-search-tool-wrapper {\n            --wln-gold: #b39964;\n            --wln-text: #1d1d1f;\n            --wln-bg-light: #fbfbfd;\n            --wln-border: #e1e1e1;\n            --wln-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\n            \n            font-family: \"Noto Serif JP\", serif;\n            color: var(--wln-text);\n            max-width: 1200px;\n            margin: 40px auto;\n            padding: 40px 20px;\n            background-color: #fff;\n            line-height: 1.6;\n        }\n\n        .wln-search-header {\n            text-align: center;\n            margin-bottom: 40px;\n        }\n\n        .wln-search-header h2 {\n            font-size: 26px;\n            font-weight: 500;\n            letter-spacing: 0.15em;\n            margin-bottom: 15px;\n            color: var(--wln-text);\n        }\n\n        .wln-search-description {\n            font-size: 15px;\n            color: #666;\n            margin-bottom: 30px;\n        }\n\n        .wln-search-form {\n            background: var(--wln-bg-light);\n            padding: 40px 20px;\n            border-radius: 8px;\n            text-align: center;\n            margin-bottom: 50px;\n        }\n\n        .wln-input-group {\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            gap: 15px;\n            flex-wrap: wrap;\n        }\n\n        .wln-date-input {\n            padding: 12px 15px;\n            border: 1px solid var(--wln-border);\n            font-size: 16px;\n            font-family: \"Montserrat\", sans-serif;\n            width: 240px;\n            border-radius: 4px;\n            outline: none;\n            background: #fff;\n        }\n\n        .wln-search-btn {\n            background: var(--wln-text);\n            color: #fff;\n            border: none;\n            padding: 13px 50px;\n            font-size: 16px;\n            font-weight: 500;\n            cursor: pointer;\n            transition: all 0.3s ease;\n            letter-spacing: 0.1em;\n            border-radius: 4px;\n            min-width: 220px;\n        }\n\n        .wln-search-btn:hover {\n            background: var(--wln-gold);\n        }\n\n        .wln-search-btn:disabled {\n            background: #ccc;\n            cursor: not-allowed;\n        }\n\n        #wln-search-status {\n            text-align: center;\n            margin: 30px 0;\n            font-size: 16px;\n            min-height: 24px;\n            font-weight: 500;\n        }\n\n        \/* Responsive Grid *\/\n        .wln-result-grid {\n            display: grid;\n            grid-template-columns: repeat(4, 1fr);\n            gap: 30px 20px;\n        }\n\n        @media (max-width: 1024px) {\n            .wln-result-grid { grid-template-columns: repeat(3, 1fr); }\n        }\n\n        @media (max-width: 767px) {\n            .wln-result-grid { grid-template-columns: repeat(2, 1fr); gap: 20px 12px; }\n            .wln-search-header h2 { font-size: 22px; }\n            .wln-date-input { width: 100%; max-width: 300px; }\n            .wln-search-btn { width: 100%; max-width: 300px; }\n        }\n\n        \/* Item Card *\/\n        .wln-item-card {\n            background: #fff;\n            border-radius: 12px;\n            overflow: hidden;\n            box-shadow: var(--wln-shadow);\n            transition: transform 0.3s ease, box-shadow 0.3s ease;\n            display: flex;\n            flex-direction: column;\n            animation: wlnFadeUp 0.6s ease forwards;\n        }\n\n        @keyframes wlnFadeUp {\n            from { opacity: 0; transform: translateY(20px); }\n            to { opacity: 1; transform: translateY(0); }\n        }\n\n        .wln-item-card:hover {\n            transform: translateY(-5px);\n            box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);\n        }\n\n        .wln-item-img-wrap {\n            aspect-ratio: 3\/4;\n            background: #f5f5f7;\n            overflow: hidden;\n            position: relative;\n        }\n\n        .wln-item-img-wrap img {\n            width: 100%;\n            height: 100%;\n            object-fit: cover;\n            transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);\n        }\n\n        .wln-item-card:hover .wln-item-img-wrap img {\n            transform: scale(1.05);\n        }\n\n        .wln-item-body {\n            padding: 20px 15px;\n            text-align: center;\n            flex-grow: 1;\n        }\n\n        .wln-item-no {\n            font-size: 14px;\n            color: #888;\n            margin-bottom: 12px;\n            font-family: \"Montserrat\", sans-serif;\n            letter-spacing: 0.05em;\n        }\n\n        .wln-detail-btn {\n            display: block;\n            background: #fff;\n            color: var(--wln-text);\n            border: 1px solid var(--wln-text);\n            padding: 10px;\n            font-size: 13px;\n            text-decoration: none;\n            transition: all 0.3s;\n            border-radius: 4px;\n            font-weight: 500;\n        }\n\n        .wln-detail-btn:hover {\n            background: var(--wln-text);\n            color: #fff;\n        }\n\n        .wln-empty-msg {\n            text-align: center;\n            padding: 60px 20px;\n            grid-column: 1 \/ -1;\n            color: #888;\n            font-size: 16px;\n        }\n    <\/style>\n\n    <div class=\"wln-search-header\">\n        <h2>\u8a2a\u554f\u7740\u30ec\u30f3\u30bf\u30eb \u7a7a\u304d\u72b6\u6cc1\u691c\u7d22<\/h2>\n        <p class=\"wln-search-description\">\u30ec\u30f3\u30bf\u30eb\u5e0c\u671b\u65e5\u3092\u9078\u629e\u3059\u308b\u3068\u3001\u305d\u306e\u65e5\u306b\u30ec\u30f3\u30bf\u30eb\u53ef\u80fd\u306a\u8a2a\u554f\u7740\u3092\u8868\u793a\u3057\u307e\u3059\u3002<\/p>\n    <\/div>\n\n    <div class=\"wln-search-form\">\n        <div class=\"wln-input-group\">\n            <input type=\"date\" id=\"wln-date-picker\" class=\"wln-date-input\">\n            <button id=\"wln-search-trigger\" class=\"wln-search-btn\">\u7a7a\u304d\u72b6\u6cc1\u3092\u691c\u7d22<\/button>\n        <\/div>\n    <\/div>\n\n    <div id=\"wln-search-status\"><\/div>\n\n    <div id=\"wln-result-area\" class=\"wln-result-grid\">\n        <!-- Results go here -->\n    <\/div>\n\n    <script>\n        (function() {\n            const CACHE_KEY = 'wln_rental_data_cache';\n            const CACHE_TTL = 15 * 60 * 1000; \/\/ 15 minutes\n\n            const productIds = [\n                \"6-56\", \"6-58\", \"6-59\", \"6-60\", \"6-61\", \"6-62\", \n                \"6-63\", \"6-64\", \"6-66\", \"6-67\", \"6-68\", \"6-69\", \"6-70\"\n            ];\n\n            const btn = document.getElementById('wln-search-trigger');\n            const dateInput = document.getElementById('wln-date-picker');\n            const statusArea = document.getElementById('wln-search-status');\n            const resultArea = document.getElementById('wln-result-area');\n\n            \/\/ Today as default min\n            dateInput.setAttribute('min', new Date().toLocaleDateString('sv-SE'));\n\n            \/**\n             * Check and get data from localStorage or fetch new\n             *\/\n            async function getRentalData() {\n                const cachedString = localStorage.getItem(CACHE_KEY);\n                if (cachedString) {\n                    const cache = JSON.parse(cachedString);\n                    if (Date.now() - cache.timestamp < CACHE_TTL) {\n                        return cache.data;\n                    }\n                }\n\n                \/\/ Parallel Fetch\n                const fetchResults = await Promise.all(\n                    productIds.map(async (id) => {\n                        try {\n                            const res = await fetch(`\/plan\/formal\/yoyaku\/${id}.html`);\n                            if (!res.ok) throw new Error();\n                            const html = await res.text();\n                            return { id, ranges: parseReservationDates(html) };\n                        } catch (e) {\n                            return { id, ranges: [], error: true };\n                        }\n                    })\n                );\n\n                const data = {};\n                fetchResults.forEach(item => {\n                    data[item.id] = item.ranges;\n                });\n\n                \/\/ Update Cache\n                localStorage.setItem(CACHE_KEY, JSON.stringify({\n                    timestamp: Date.now(),\n                    data: data\n                }));\n\n                return data;\n            }\n\n            \/**\n             * Extract dates using regex\n             *\/\n            function parseReservationDates(html) {\n                const regex = \/(\\d{4}\\\/\\d{1,2}\\\/\\d{1,2})\uff5e(\\d{4}\\\/\\d{1,2}\\\/\\d{1,2})\/g;\n                const ranges = [];\n                let match;\n                while ((match = regex.exec(html)) !== null) {\n                    \/\/ Normalize to YYYY-MM-DD\n                    const start = match[1].replace(\/\\\/\/g, '-');\n                    const end = match[2].replace(\/\\\/\/g, '-');\n                    ranges.push([start, end]);\n                }\n                return ranges;\n            }\n\n            \/**\n             * Availability Check Logic\n             *\/\n            function isAvailable(targetDate, ranges) {\n                if (!ranges || ranges.length === 0) return true;\n\n                const targetTime = targetDate.getTime();\n\n                for (const range of ranges) {\n                    const rStart = new Date(range[0]);\n                    const rEnd = new Date(range[1]);\n\n                    \/\/ Calculation Rule\n                    \/\/ Unavailability Start = Reserved Start - 4 days\n                    const unavailStart = new Date(rStart);\n                    unavailStart.setDate(unavailStart.getDate() - 4);\n                    \n                    \/\/ Unavailability End = Reserved End - 2 days\n                    const unavailEnd = new Date(rEnd);\n                    unavailEnd.setDate(unavailEnd.getDate() - 2);\n\n                    if (targetTime >= unavailStart.getTime() && targetTime <= unavailEnd.getTime()) {\n                        return false;\n                    }\n                }\n                return true;\n            }\n\n            \/**\n             * Search Event\n             *\/\n            btn.addEventListener('click', async () => {\n                const dateVal = dateInput.value;\n                if (!dateVal) {\n                    statusArea.innerHTML = '<span style=\"color:#d93025;\">\u65e5\u4ed8\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/span>';\n                    return;\n                }\n\n                const targetDate = new Date(dateVal);\n                targetDate.setHours(0, 0, 0, 0);\n\n                btn.disabled = true;\n                statusArea.innerText = \"\u691c\u7d22\u4e2d...\";\n                resultArea.innerHTML = \"\";\n\n                try {\n                    const allData = await getRentalData();\n                    let count = 0;\n\n                    productIds.forEach(id => {\n                        if (isAvailable(targetDate, allData[id])) {\n                            renderCard(id);\n                            count++;\n                        }\n                    });\n\n                    if (count > 0) {\n                        statusArea.innerText = `\u691c\u7d22\u5b8c\u4e86\uff1a${count}\u7740\u306e\u30ec\u30f3\u30bf\u30eb\u304c\u53ef\u80fd\u3067\u3059\u3002`;\n                    } else {\n                        resultArea.innerHTML = '<div class=\"wln-empty-msg\">\u3054\u6307\u5b9a\u306e\u65e5\u7a0b\u306f\u3001\u3059\u3079\u3066\u306e\u7740\u7269\u304c\u3054\u4e88\u7d04\u6e08\u307f\u3067\u3059\u3002<\/div>';\n                        statusArea.innerText = \"\";\n                    }\n                } catch (err) {\n                    statusArea.innerText = \"\u73fe\u5728\u691c\u7d22\u3067\u304d\u307e\u305b\u3093\u3002\";\n                    console.error(err);\n                } finally {\n                    btn.disabled = false;\n                }\n            });\n\n            \/**\n             * Render Item Card\n             *\/\n            function renderCard(id) {\n                const card = document.createElement('div');\n                card.className = 'wln-item-card';\n                card.innerHTML = `\n                    <div class=\"wln-item-img-wrap\">\n                        <img decoding=\"async\" src=\"https:\/\/walin.jp\/plan\/formal\/images\/${id}.jpg\" alt=\"No. ${id}\" loading=\"lazy\">\n                    <\/div>\n                    <div class=\"wln-item-body\">\n                        <div class=\"wln-item-no\">No. ${id}<\/div>\n                        <a href=\"https:\/\/walin.jp\/plan\/formal\/syoukai\/${id}.html\" target=\"_blank\" class=\"wln-detail-btn\">\u8a73\u7d30\u3092\u898b\u308b<\/a>\n                    <\/div>\n                `;\n                resultArea.appendChild(card);\n            }\n        })();\n    <\/script>\n<\/div>\n","protected":false},"excerpt":{"rendered":"\u8a2a\u554f\u7740\u30ec\u30f3\u30bf\u30eb \u7a7a\u304d\u72b6\u6cc1\u691c\u7d22 \u30ec\u30f3\u30bf\u30eb\u5e0c\u671b\u65e5\u3092\u9078\u629e\u3059\u308b\u3068\u3001\u305d\u306e\u65e5\u306b\u30ec\u30f3\u30bf\u30eb\u53ef\u80fd\u306a\u8a2a\u554f\u7740\u3092\u8868\u793a\u3057\u307e\u3059\u3002 \u7a7a\u304d\u72b6\u6cc1\u3092\u691c\u7d22","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-10999","post","type-post","status-publish","format-standard","hentry","category-1"],"acf":[],"_links":{"self":[{"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/posts\/10999","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/comments?post=10999"}],"version-history":[{"count":2,"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/posts\/10999\/revisions"}],"predecessor-version":[{"id":11002,"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/posts\/10999\/revisions\/11002"}],"wp:attachment":[{"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/media?parent=10999"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/categories?post=10999"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/walin.jp\/kimonovoice\/wp-json\/wp\/v2\/tags?post=10999"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}