๐Ÿ‰ Royal Patchwork Dragon DIY Kit · Stitch Your Own Fantasy Guardian

$27.97 $38.99 Save $11.02
/** * ไผ˜ๆƒ ็ ็ป„ไปถๆจกๅž‹็ฑป * ๅค„็†ไผ˜ๆƒ ็ ็š„ๆ˜พ็คบๅ’Œไบคไบ’้€ป่พ‘ */ class SpzCustomDiscountCodeModel extends SPZ.BaseElement { constructor(element) { super(element); // ๅคๅˆถๆŒ‰้’ฎๅ’Œๅ†…ๅฎน็š„็ฑปๅ this.copyBtnClass = "discount_code_btn" this.copyClass = "discount_code_value" } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { // ๅˆๅง‹ๅŒ–ๆœๅŠก this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); } /** * ๆธฒๆŸ“ไผ˜ๆƒ ็ ็ป„ไปถ * @param {Object} data - ๆธฒๆŸ“ๆ•ฐๆฎ */ doRender_(data) { return this.templates_ .findAndRenderTemplate(this.element, Object.assign(this.getDefaultData(), data) ) .then((el) => { this.clearDom(); this.element.appendChild(el); // ็ป‘ๅฎšๅคๅˆถไปฃ็ ๅŠŸ่ƒฝ this.copyCode(el, data); }); } /** * ่Žทๅ–ๆธฒๆŸ“ๆจกๆฟ * @param {Object} data - ๆธฒๆŸ“ๆ•ฐๆฎ */ getRenderTemplate(data) { const renderData = Object.assign(this.getDefaultData(), data); return this.templates_ .findAndRenderTemplate(this.element, renderData) .then((el) => { this.clearDom(); return el; }); } /** * ๆธ…้™คDOMๅ†…ๅฎน */ clearDom() { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); } /** * ่Žทๅ–้ป˜่ฎคๆ•ฐๆฎ * @returns {Object} ้ป˜่ฎคๆ•ฐๆฎๅฏน่ฑก */ getDefaultData() { return { isMobile: appDiscountUtils.judgeMobile(), isRTL: appDiscountUtils.judgeRTL(), image_domain: this.win.SHOPLAZZA.image_domain, copyBtnClass: this.copyBtnClass, copyClass: this.copyClass } } /** * ๅคๅˆถไผ˜ๆƒ ็ ๅŠŸ่ƒฝ * @param {Element} el - ๅฝ“ๅ‰ๅ…ƒ็ด  */ copyCode(el) { const copyBtnList = el.querySelectorAll(`.${this.copyBtnClass}`); if (copyBtnList.length > 0) { copyBtnList.forEach(item => { item.onclick = async () => { // ็กฎไฟ่Žทๅ–ๆญฃ็กฎ็š„ๅ…ƒ็ด ๅ’Œๅ†…ๅฎน const codeElement = item.querySelector(`.${this.copyClass}`); if (!codeElement) return; // ่Žทๅ–็บฏๆ–‡ๆœฌๅ†…ๅฎน const textToCopy = codeElement.innerText.trim(); // ๅฐ่ฏ•ไฝฟ็”จ็ŽฐไปฃAPI๏ผŒๅฆ‚ๆžœๅคฑ่ดฅๅˆ™ไฝฟ็”จๅค‡็”จๆ–นๆกˆ try { if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(textToCopy); } else { throw new Error('Clipboard API not available'); } // ๆ˜พ็คบๅคๅˆถๆˆๅŠŸๆ็คบ this.showCopySuccessToast(textToCopy, el); } catch (err) { console.error('Modern clipboard API failed, trying fallback...', err); // ไฝฟ็”จๅค‡็”จๅคๅˆถๆ–นๆกˆ this.fallbackCopy(textToCopy, el); } const discountId = item.dataset["discountId"]; // ่ทณ่ฝฌๅ†ณ็ญ–: is_redirection + link(ๅฏ้€‰่ฆ†็›–) const setting = { is_redirection: item.dataset["redirection"] === "true", link: item.dataset["link"], }; const landingUrl = `/promotions/discount-default/${discountId}`; const finalUrl = appDiscountUtils.resolveDiscountHref(setting, landingUrl); if (finalUrl && appDiscountUtils.inProductBody(this.element)) { this.win.open(finalUrl, '_blank', 'noopener'); } } }) } } /** * ไฝฟ็”จ execCommand ็š„ๅคๅˆถๆ–นๆกˆ * @param {string} codeText - ่ฆๅคๅˆถ็š„ๆ–‡ๆœฌ * @param {Element} el - ๅฝ“ๅ‰ๅ…ƒ็ด  */ fallbackCopy(codeText, el) { const textarea = this.win.document.createElement('textarea'); textarea.value = codeText; // ่ฎพ็ฝฎๆ ทๅผไฝฟๆ–‡ๆœฌๆก†ไธๅฏ่ง textarea.style.position = 'fixed'; textarea.style.left = '-9999px'; textarea.style.top = '0'; // ๆทปๅŠ  readonly ๅฑžๆ€ง้˜ฒๆญข็งปๅŠจ็ซฏ่™šๆ‹Ÿ้”ฎ็›˜ๅผนๅ‡บ textarea.setAttribute('readonly', 'readonly'); this.win.document.body.appendChild(textarea); textarea.focus(); textarea.select(); try { this.win.document.execCommand('copy'); // ๆ˜พ็คบๅคๅˆถๆˆๅŠŸๆ็คบ this.showCopySuccessToast(codeText, el); } catch (err) { console.error('Copy failed:', err); } this.win.document.body.removeChild(textarea); } /** * ๅˆ›ๅปบ Toast ๅ…ƒ็ด  * @returns {Element} ๅˆ›ๅปบ็š„ Toast ๅ…ƒ็ด  */ createToastEl_() { const toast = document.createElement('ljs-toast'); toast.setAttribute('layout', 'nodisplay'); toast.setAttribute('hidden', ''); toast.setAttribute('id', 'discount-code-toast'); toast.style.zIndex = '1051'; return toast; } /** * ๆŒ‚่ฝฝ Toast ๅ…ƒ็ด ๅˆฐ body * @returns {Element} ๆŒ‚่ฝฝ็š„ Toast ๅ…ƒ็ด  */ mountToastToBody_() { const existingToast = this.win.document.getElementById('discount-code-toast'); if (existingToast) { return existingToast; } const toast = this.createToastEl_(); this.win.document.body.appendChild(toast); return toast; } /** * ๅคๅˆถๆˆๅŠŸ็š„ๆ้†’ * @param {string} codeText - ่ฆๅคๅˆถ็š„ๆ–‡ๆœฌ * @param {Element} el - ๅฝ“ๅ‰ๅ…ƒ็ด  */ showCopySuccessToast(codeText, el) { const $toast = this.mountToastToBody_(); SPZ.whenApiDefined($toast).then(toast => { toast.showToast("Discount code copied !"); this.codeCopyInSessionStorage(codeText); }); } /** * ๅคๅˆถไผ˜ๆƒ ็ ๆˆๅŠŸๅŽ่ฆๅญ˜ไธ€ไปฝๅˆฐๆœฌๅœฐๅญ˜ๅ‚จไธญ๏ผŒ่ดญ็‰ฉ่ฝฆไฝฟ็”จ * @param {string} codeText - ่ฆๅคๅˆถ็š„ๆ–‡ๆœฌ */ codeCopyInSessionStorage(codeText) { try { sessionStorage.setItem('other-copied-coupon', codeText); } catch (error) { console.error(error) } } } // ๆณจๅ†Œ่‡ชๅฎšไน‰ๅ…ƒ็ด  SPZ.defineElement('spz-custom-discount-code-model', SpzCustomDiscountCodeModel);
/** * Custom discount code component that handles displaying and managing discount codes * @extends {SPZ.BaseElement} */ class SpzCustomDiscountCode extends SPZ.BaseElement { constructor(element) { super(element); // API endpoint for fetching discount codes this.getDiscountCodeApi = "\/api\/storefront\/promotion\/code\/list"; // Debounce timer for resize events this.timer = null; // Current variant ID this.variantId = "b544f7c8-5da5-446e-a065-7319b99730c7"; // Store discount code data this.discountCodeData = {} } /** * Check if layout is supported * @param {string} layout - Layout type * @return {boolean} */ isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } /** * Initialize component after build */ buildCallback() { this.templates_ = SPZServices.templatesForDoc(); this.viewport_ = this.getViewport(); // Bind methods to maintain context this.render = this.render.bind(this); this.resize = this.resize.bind(this); this.switchVariant = this.switchVariant.bind(this); } /** * Setup component when mounted */ mountCallback() { this.getData(); // Add event listeners this.viewport_.onResize(this.resize); this.win.document.addEventListener('dj.variantChange', this.switchVariant); } /** * Cleanup when component is unmounted */ unmountCallback() { this.viewport_.removeResize(this.resize); this.win.document.removeEventListener('dj.variantChange', this.switchVariant); // ๆธ…้™คๅฎšๆ—ถๅ™จ if (this.timer) { clearTimeout(this.timer); this.timer = null; } } /** * Handle resize events with debouncing */ resize() { if (this.timer) { clearTimeout(this.timer) this.timer = null; } this.timer = setTimeout(() => { if (appDiscountUtils.inProductBody(this.element)) { this.render(); } else { this.renderSkeleton(); } }, 200); } /** * Handle variant changes * @param {Event} event - Variant change event */ switchVariant(event) { const variant = event.detail.selected; if (variant.product_id == 'f0565c27-085a-4689-85d2-b7e91460bb28' && variant.id != this.variantId) { this.variantId = variant.id; this.getData(); } } /** * Fetch discount code data from API */ getData() { if (appDiscountUtils.inProductBody(this.element)) { const reqBody = { product_id: "f0565c27-085a-4689-85d2-b7e91460bb28", variant_id: this.variantId, product_type: "default", } if (!reqBody.product_id || !reqBody.variant_id) return; this.discountCodeData = {}; this.win.fetch(this.getDiscountCodeApi, { method: "POST", body: JSON.stringify(reqBody), headers: { "Content-Type": "application/json" } }).then(async (response) => { if (response.ok) { let data = await response.json(); if (data.list && data.list.length > 0) { data.list[0].product_setting.template_config = JSON.parse(data.list[0].product_setting.template_config); // Format timestamps to local timezone const zone = this.win.SHOPLAZZA.shop.time_zone; data.list = data.list.map(item => { if(+item.ends_at !== -1) { item.ends_at = appDiscountUtils.convertTimestampToFormat(+item.ends_at, zone); } item.starts_at = appDiscountUtils.convertTimestampToFormat(+item.starts_at, zone); return item; }); } this.discountCodeData = data; this.render(); } else { this.clearDom(); } }).catch(err => { console.error("discount_code", err) this.clearDom(); }); } else { this.renderSkeleton(); } } /** * Clear component DOM except template */ clearDom() { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); } /** * Render discount codes with formatted dates */ render() { // Render using discount code model SPZ.whenApiDefined(document.querySelector('#spz_custom_discount_code_model')).then(renderApi => { renderApi.doRender_({ discountCodeData: this.discountCodeData }) }).catch(err => { this.clearDom(); }) } renderSkeleton() { // Render template for non-product pages this.templates_ .findAndRenderTemplate(this.element, { isMobile: appDiscountUtils.judgeMobile() }) .then((el) => { this.clearDom(); this.element.appendChild(el); }) .catch(err => { this.clearDom(); }); } } // Register custom element SPZ.defineElement('spz-custom-discount-code', SpzCustomDiscountCode);
Choose Your Dragon:  โ˜€๏ธ Ivory Sun
Quantity
Share the love
Free worldwide shipping
Free returns
Sustainably made
Secure payments
Description

โœ… DIY patchwork dragon kit
โœ… Multiple collectible dragon styles
โœ… Beginner friendly sewing project
โœ… Perfect for cozy nights, craft lovers, and fantasy fans
โœ… A finished piece you can display on a desk, shelf, or craft corner

๐Ÿ‰ Choose Your Dragon Guardian

Pick the dragon that feels like yours. From soft floral styles to moonlit fantasy colors, each patchwork dragon has its own fabric story, personality, and collectible look. Choose one favorite, or build the full handmade dragon family.

๐ŸŽ A Beautiful Kit From the First Look

Each kit arrives in a giftable box designed to match the dragon inside. The packaging shows the style clearly, while the finished sample helps you imagine what your own handmade dragon can become after sewing, stuffing, and shaping.

๐Ÿงต Everything You Need to Start

Inside the kit, you’ll receive matching fabric pieces, thread, needle, stuffing, small findings, and a step by step guide. Every color and detail is selected to match the dragon style, so your finished piece keeps its own magical handmade charm.

๐Ÿชก Relaxing, Slow, and Satisfying

Take your time and enjoy the process. Stitch the pieces together, add the stuffing, shape the body, wings, horns, and tail, then watch your little dragon come to life one step at a time.

๐Ÿก Made to Be Displayed

Once finished, your dragon becomes a soft handmade display piece for a desk, shelf, craft room, reading nook, or bedside table. It feels more meaningful because you made it yourself, making it a charming keepsake or thoughtful gift for any craft lover.

โ“ FAQ

1. Is this a finished dragon or a DIY kit?
This is a DIY kit. You will receive the materials needed to create the dragon yourself.

2. Is it beginner friendly?
Yes. It is beginner friendly, though some patience is needed because the dragon includes detailed parts like wings, tail, horns, and body sections.

3. What comes in the kit?
Each kit includes matching fabric pieces, thread, needle, stuffing, small findings, and a step by step guide.

๐Ÿ“ฆ Product Details

Product Name: Royal Patchwork Dragon DIY Kit
Style: DIY sewing and patchwork craft kit
Finished Use: Desk decor, shelf decor, craft room display, handmade gift
Skill Level: Beginner friendly, patience recommended
Kit Includes: Fabric pieces, thread, needle, stuffing, small findings, and step by step guide
Available Styles: Midnight Shadow, Crimson Flame, Cardinal Guardian, Ivory Sun, Blue Willow, Emerald Forest, Rose Garden, Lavender Moon
Recommended Age: 12+ due to small parts and needle use