UNPKG

@mayurgodhani/ecomtools-cli

Version:

E-commerce tools MCP server for Shopify development

355 lines (314 loc) 9.93 kB
#!/usr/bin/env node /** * Setup script for ecomTools * Runs during npm installation to ensure all required files and directories exist */ const fs = require('fs'); const path = require('path'); // Paths to ensure exist const directories = [ path.join(__dirname, '../data') ]; // Ensure required directories exist directories.forEach(dir => { if (!fs.existsSync(dir)) { console.log(`Creating directory: ${dir}`); fs.mkdirSync(dir, { recursive: true }); } }); // Create sample data files if they don't exist const sampleDataFiles = [ { path: path.join(__dirname, '../data/cart-drawer.txt'), content: `Title: Shopify Cart Drawer Description: A responsive and customizable cart drawer for Shopify themes with quantity controls, subtotals, and checkout button. Code: <!-- Cart Drawer HTML --> <div id="cart-drawer" class="cart-drawer"> <div class="cart-drawer__header"> <h2>Your Cart</h2> <button class="cart-drawer__close" aria-label="Close cart"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> </div> <div class="cart-drawer__content"> {%- if cart.item_count > 0 -%} <form action="{{ routes.cart_url }}" method="post" id="cart-drawer-form"> {%- for item in cart.items -%} <div class="cart-drawer__item" data-item-key="{{ item.key }}"> <div class="cart-drawer__item-image"> <img src="{{ item.image | img_url: '120x' }}" alt="{{ item.title | escape }}" width="70" height="70"> </div> <div class="cart-drawer__item-details"> <a href="{{ item.url }}" class="cart-drawer__item-title"> {{ item.product.title }} </a> <div class="cart-drawer__item-variant"> {{ item.variant.title }} </div> <div class="cart-drawer__item-price"> {{ item.final_price | money }} </div> <div class="cart-drawer__item-quantity"> <button type="button" class="quantity-button" data-action="decrease">-</button> <input type="number" name="updates[{{ item.key }}]" value="{{ item.quantity }}" min="0" aria-label="Quantity"> <button type="button" class="quantity-button" data-action="increase">+</button> </div> <button type="button" class="cart-drawer__item-remove" data-item-key="{{ item.key }}"> Remove </button> </div> </div> {%- endfor -%} <div class="cart-drawer__footer"> <div class="cart-drawer__subtotal"> <span>Subtotal</span> <span>{{ cart.total_price | money }}</span> </div> <div class="cart-drawer__taxes"> <small>Tax included. <a href="{{ shop.policies.shipping_policy.url }}">Shipping</a> calculated at checkout.</small> </div> <button type="submit" name="checkout" class="cart-drawer__checkout-button"> Checkout </button> </div> </form> {%- else -%} <div class="cart-drawer__empty"> <p>Your cart is currently empty.</p> <a href="{{ routes.all_products_collection_url }}" class="cart-drawer__continue-shopping"> Continue Shopping </a> </div> {%- endif -%} </div> </div> <div id="cart-overlay" class="cart-overlay"></div> <script> document.addEventListener('DOMContentLoaded', function() { // Cart drawer functionality const cartDrawer = document.getElementById('cart-drawer'); const cartToggle = document.querySelectorAll('.cart-toggle'); const cartClose = document.querySelector('.cart-drawer__close'); const cartOverlay = document.getElementById('cart-overlay'); // Open cart drawer cartToggle.forEach(function(toggle) { toggle.addEventListener('click', function(e) { e.preventDefault(); cartDrawer.classList.add('cart-drawer--open'); document.body.classList.add('cart-drawer-open'); cartOverlay.classList.add('cart-overlay--active'); }); }); // Close cart drawer if (cartClose) { cartClose.addEventListener('click', function() { cartDrawer.classList.remove('cart-drawer--open'); document.body.classList.remove('cart-drawer-open'); cartOverlay.classList.remove('cart-overlay--active'); }); } if (cartOverlay) { cartOverlay.addEventListener('click', function() { cartDrawer.classList.remove('cart-drawer--open'); document.body.classList.remove('cart-drawer-open'); cartOverlay.classList.remove('cart-overlay--active'); }); } // Handle quantity buttons const quantityButtons = document.querySelectorAll('.quantity-button'); quantityButtons.forEach(function(button) { button.addEventListener('click', function() { const input = this.parentNode.querySelector('input'); const currentValue = parseInt(input.value); if (this.dataset.action === 'increase') { input.value = currentValue + 1; } else if (this.dataset.action === 'decrease' && currentValue > 1) { input.value = currentValue - 1; } // You would typically have AJAX cart update here }); }); }); </script> <style> .cart-drawer { position: fixed; top: 0; right: 0; width: 400px; max-width: 90vw; height: 100vh; background-color: #fff; box-shadow: -2px 0 10px rgba(0,0,0,0.1); z-index: 1000; transform: translateX(100%); transition: transform 0.3s ease-in-out; } .cart-drawer--open { transform: translateX(0); } /* Add your additional CSS here */ </style> ` }, { path: path.join(__dirname, '../data/product-card.txt'), content: `Title: Shopify Product Card Description: A responsive product card component for displaying products in collections with image, title, price, and add to cart button. Code: <div class="product-card"> <a href="{{ product.url | within: collection }}" class="product-card__link"> <div class="product-card__image-wrapper"> {% if product.featured_image %} <img class="product-card__image" src="{{ product.featured_image | img_url: '400x' }}" alt="{{ product.featured_image.alt | escape }}" width="400" height="400" loading="lazy" > {% else %} {{ 'product-1' | placeholder_svg_tag: 'product-card__image placeholder-svg' }} {% endif %} {% if product.compare_at_price > product.price %} <span class="product-card__badge">Sale</span> {% endif %} </div> <div class="product-card__info"> <h3 class="product-card__title">{{ product.title }}</h3> <div class="product-card__price"> {% if product.compare_at_price > product.price %} <span class="product-card__price-regular">{{ product.price | money }}</span> <span class="product-card__price-compare">{{ product.compare_at_price | money }}</span> {% else %} <span class="product-card__price-regular">{{ product.price | money }}</span> {% endif %} </div> </div> </a> <div class="product-card__actions"> {% if product.available %} <button class="product-card__add-to-cart" data-product-id="{{ product.id }}" data-variant-id="{{ product.selected_or_first_available_variant.id }}" > Add to cart </button> {% else %} <button class="product-card__sold-out" disabled>Sold out</button> {% endif %} </div> </div> <style> .product-card { position: relative; margin-bottom: 30px; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 5px rgba(0,0,0,0.1); transition: transform 0.3s, box-shadow 0.3s; } .product-card:hover { transform: translateY(-5px); box-shadow: 0 5px 15px rgba(0,0,0,0.1); } .product-card__link { display: block; text-decoration: none; color: inherit; } .product-card__image-wrapper { position: relative; padding-top: 100%; /* 1:1 Aspect Ratio */ overflow: hidden; } .product-card__image { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s; } .product-card:hover .product-card__image { transform: scale(1.05); } .product-card__badge { position: absolute; top: 10px; right: 10px; background-color: #e53e3e; color: white; padding: 5px 10px; border-radius: 4px; font-size: 0.75rem; font-weight: bold; } .product-card__info { padding: 15px; background-color: white; } .product-card__title { margin: 0 0 10px; font-size: 1rem; font-weight: 500; } .product-card__price { display: flex; align-items: center; flex-wrap: wrap; font-weight: 500; } .product-card__price-regular { font-size: 1rem; } .product-card__price-compare { margin-left: 8px; font-size: 0.875rem; color: #666; text-decoration: line-through; } .product-card__actions { padding: 0 15px 15px; background-color: white; } .product-card__add-to-cart, .product-card__sold-out { width: 100%; padding: 10px 15px; border: none; border-radius: 4px; font-size: 0.875rem; font-weight: 500; cursor: pointer; transition: background-color 0.2s; } .product-card__add-to-cart { background-color: #3182ce; color: white; } .product-card__add-to-cart:hover { background-color: #2c5282; } .product-card__sold-out { background-color: #e2e8f0; color: #4a5568; cursor: not-allowed; } </style> ` } ]; sampleDataFiles.forEach(file => { if (!fs.existsSync(file.path)) { console.log(`Creating sample data file: ${file.path}`); fs.writeFileSync(file.path, file.content); } }); console.log('Setup complete!');