From ade0ed1a6785e59cbc7eff2b3b303d5ad1cde6b3 Mon Sep 17 00:00:00 2001 From: isUnknown Date: Fri, 16 Jan 2026 12:44:28 +0100 Subject: [PATCH] Add virtual pages from Shopify and panel refresh button This simplifies product management by eliminating manual Kirby page creation. Products are now automatically loaded as virtual pages from the Shopify API. Changes: - Add virtual pages via page.children:after hook - Create shopify.php helper with caching (60min TTL) - Add shopify-refresh panel plugin for cache management - Remove manual product content files (now virtual) - Update site.yml blueprint to show refresh button - Fix cache implementation to use get/set pattern Co-Authored-By: Claude Sonnet 4.5 --- .claude/settings.local.json | 3 +- .../product.en.txt | 5 - .../product.fr.txt | 9 -- content/2_t-shirt-index/product.en.txt | 5 - content/2_t-shirt-index/product.fr.txt | 9 -- site/blueprints/site.yml | 20 ++- site/config/config.php | 32 +++++ site/config/shopify.php | 81 ++++++++++++ .../shopify-refresh-button/.editorconfig | 20 +++ .../shopify-refresh-button/.gitattributes | 11 ++ .../plugins/shopify-refresh-button/.gitignore | 14 +++ .../plugins/shopify-refresh-button/LICENSE.md | 21 ++++ site/plugins/shopify-refresh-button/README.md | 117 ++++++++++++++++++ .../shopify-refresh-button/SECURITY.md | 18 +++ .../shopify-refresh-button/composer.json | 21 ++++ .../shopify-refresh-button/composer.lock | 66 ++++++++++ site/plugins/shopify-refresh-button/index.js | 2 + site/plugins/shopify-refresh-button/index.php | 46 +++++++ .../shopify-refresh-button/package.json | 7 ++ .../src/components/ShopifyRefreshButton.vue | 97 +++++++++++++++ .../shopify-refresh-button/src/index.js | 7 ++ 21 files changed, 570 insertions(+), 41 deletions(-) delete mode 100644 content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.en.txt delete mode 100644 content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.fr.txt delete mode 100644 content/2_t-shirt-index/product.en.txt delete mode 100644 content/2_t-shirt-index/product.fr.txt create mode 100644 site/config/shopify.php create mode 100644 site/plugins/shopify-refresh-button/.editorconfig create mode 100644 site/plugins/shopify-refresh-button/.gitattributes create mode 100644 site/plugins/shopify-refresh-button/.gitignore create mode 100755 site/plugins/shopify-refresh-button/LICENSE.md create mode 100755 site/plugins/shopify-refresh-button/README.md create mode 100644 site/plugins/shopify-refresh-button/SECURITY.md create mode 100755 site/plugins/shopify-refresh-button/composer.json create mode 100644 site/plugins/shopify-refresh-button/composer.lock create mode 100644 site/plugins/shopify-refresh-button/index.js create mode 100755 site/plugins/shopify-refresh-button/index.php create mode 100644 site/plugins/shopify-refresh-button/package.json create mode 100644 site/plugins/shopify-refresh-button/src/components/ShopifyRefreshButton.vue create mode 100755 site/plugins/shopify-refresh-button/src/index.js diff --git a/.claude/settings.local.json b/.claude/settings.local.json index bdbdb91..5c985c5 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -7,7 +7,8 @@ "Bash(find:*)", "Bash(curl:*)", "WebFetch(domain:snipcart.com)", - "Bash(grep:*)" + "Bash(grep:*)", + "Bash(npm run build:*)" ] } } diff --git a/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.en.txt b/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.en.txt deleted file mode 100644 index 6bbc828..0000000 --- a/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.en.txt +++ /dev/null @@ -1,5 +0,0 @@ -Title: Éclairages : 12 entretiens et analyses sur les violences d'État - ----- - -Uuid: gzshayl6xoefrnsz diff --git a/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.fr.txt b/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.fr.txt deleted file mode 100644 index f26cd4f..0000000 --- a/content/1_eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat/product.fr.txt +++ /dev/null @@ -1,9 +0,0 @@ -Title: Éclairages : 12 entretiens et analyses sur les violences d’État - ----- - -Shopifyhandle: eclairages-12-entretiens-et-analyses-sur-les-violences-d-etat - ----- - -Uuid: gzshayl6xoefrnsz \ No newline at end of file diff --git a/content/2_t-shirt-index/product.en.txt b/content/2_t-shirt-index/product.en.txt deleted file mode 100644 index 7aa8f14..0000000 --- a/content/2_t-shirt-index/product.en.txt +++ /dev/null @@ -1,5 +0,0 @@ -Title: T-shirt Index - ----- - -Uuid: qq27mjjpethsvnwp diff --git a/content/2_t-shirt-index/product.fr.txt b/content/2_t-shirt-index/product.fr.txt deleted file mode 100644 index 395184c..0000000 --- a/content/2_t-shirt-index/product.fr.txt +++ /dev/null @@ -1,9 +0,0 @@ -Title: T-shirt Index - ----- - -Shopifyhandle: t-shirt-index-01 - ----- - -Uuid: qq27mjjpethsvnwp \ No newline at end of file diff --git a/site/blueprints/site.yml b/site/blueprints/site.yml index df442f5..13b775f 100644 --- a/site/blueprints/site.yml +++ b/site/blueprints/site.yml @@ -1,15 +1,11 @@ title: Site sections: - pages: - type: pages - headline: - en: Products - fr: Produits - template: product - sortBy: title asc - info: "{{ page.stock }} en stock" - layout: cardlets - image: - query: page.files.first - cover: true + shopify: + type: fields + fields: + shopifyRefreshButton: + type: shopify-refresh + label: + en: Shopify Products + fr: Produits Shopify diff --git a/site/config/config.php b/site/config/config.php index 427755b..792e4a3 100644 --- a/site/config/config.php +++ b/site/config/config.php @@ -1,10 +1,16 @@ true, 'languages' => true, + 'cache' => [ + 'shopify' => true + ], + 'thumbs' => [ 'quality' => 85, 'format' => 'webp', @@ -147,5 +153,31 @@ return [ } ] */ + ], + + 'hooks' => [ + 'page.children:after' => function ($children, $page) { + if ($page->isHomePage()) { + $products = getShopifyProducts(); + + foreach ($products as $product) { + $children = $children->add([ + \Kirby\Cms\Page::factory([ + 'slug' => $product['handle'], + 'template' => 'product', + 'model' => 'product', + 'num' => 0, + 'parent' => $page, + 'content' => [ + 'title' => $product['title'], + 'shopifyHandle' => $product['handle'] + ] + ]) + ]); + } + } + + return $children; + } ] ]; diff --git a/site/config/shopify.php b/site/config/shopify.php new file mode 100644 index 0000000..d111b49 --- /dev/null +++ b/site/config/shopify.php @@ -0,0 +1,81 @@ +cache('shopify'); + $products = $cache->get('products'); + + if ($products === null) { + $products = fetchShopifyProducts(); + $cache->set('products', $products, 60); // Cache 60 minutes + } + + return $products; +} + +/** + * Appel direct à l'API Shopify Storefront + */ +function fetchShopifyProducts(): array +{ + $domain = 'nv7cqv-bu.myshopify.com'; + $token = 'dec3d35a2554384d149c72927d1cfd1b'; + $apiVersion = '2026-01'; + $endpoint = "https://{$domain}/api/{$apiVersion}/graphql.json"; + + $query = ' + query getAllProducts { + products(first: 250, sortKey: TITLE) { + edges { + node { + id + handle + title + } + } + } + } + '; + + $ch = curl_init($endpoint); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + 'X-Shopify-Storefront-Access-Token: ' . $token + ]); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ + 'query' => $query + ])); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode !== 200) { + error_log("Shopify API error: HTTP {$httpCode}"); + return []; + } + + $data = json_decode($response, true); + + if (isset($data['errors'])) { + error_log("Shopify API GraphQL errors: " . json_encode($data['errors'])); + return []; + } + + $products = []; + foreach ($data['data']['products']['edges'] as $edge) { + $node = $edge['node']; + $products[] = [ + 'id' => $node['id'], + 'handle' => $node['handle'], + 'title' => $node['title'] + ]; + } + + return $products; +} diff --git a/site/plugins/shopify-refresh-button/.editorconfig b/site/plugins/shopify-refresh-button/.editorconfig new file mode 100644 index 0000000..3b762c9 --- /dev/null +++ b/site/plugins/shopify-refresh-button/.editorconfig @@ -0,0 +1,20 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.php] +indent_size = 4 + +[*.md,*.txt] +trim_trailing_whitespace = false +insert_final_newline = false + +[composer.json] +indent_size = 4 diff --git a/site/plugins/shopify-refresh-button/.gitattributes b/site/plugins/shopify-refresh-button/.gitattributes new file mode 100644 index 0000000..033ba13 --- /dev/null +++ b/site/plugins/shopify-refresh-button/.gitattributes @@ -0,0 +1,11 @@ +# Note: You need to uncomment the lines you want to use; the other lines can be deleted + +# Git +# .gitattributes export-ignore +# .gitignore export-ignore + +# Tests +# /.coveralls.yml export-ignore +# /.travis.yml export-ignore +# /phpunit.xml.dist export-ignore +# /tests/ export-ignore diff --git a/site/plugins/shopify-refresh-button/.gitignore b/site/plugins/shopify-refresh-button/.gitignore new file mode 100644 index 0000000..4d81cf5 --- /dev/null +++ b/site/plugins/shopify-refresh-button/.gitignore @@ -0,0 +1,14 @@ +# OS files +.DS_Store + +# npm modules +/node_modules + +# Parcel cache folder +.cache + +# Composer files +/vendor + +# kirbyup temp development entry +/index.dev.mjs diff --git a/site/plugins/shopify-refresh-button/LICENSE.md b/site/plugins/shopify-refresh-button/LICENSE.md new file mode 100755 index 0000000..8e663d7 --- /dev/null +++ b/site/plugins/shopify-refresh-button/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/site/plugins/shopify-refresh-button/README.md b/site/plugins/shopify-refresh-button/README.md new file mode 100755 index 0000000..ad2b202 --- /dev/null +++ b/site/plugins/shopify-refresh-button/README.md @@ -0,0 +1,117 @@ +# Kirby Pluginkit: Example plugin for Kirby + +> Variant "Panel plugin setup" + +This is a boilerplate for a Kirby Panel plugin that can be installed via all three [supported installation methods](https://getkirby.com/docs/guide/plugins/plugin-setup-basic#the-three-plugin-installation-methods). + +You can find a list of Pluginkit variants on the [`master` branch](https://github.com/getkirby/pluginkit/tree/master). + +**** + +## How to use the Pluginkit + +1. Fork this repository +2. Change the plugin name and description in the `composer.json` +3. Change the plugin name in the `index.php` and `src/index.js` +4. Change the license if you don't want to publish under MIT +5. Add your plugin code to the `index.php` and `src/index.js` +6. Update this `README` with instructions for your plugin + +### Install the development and build setup + +We use [kirbyup](https://github.com/johannschopplich/kirbyup) for the development and build setup. + +You can start developing directly. kirbyup will be fetched remotely with your first `npm run` command, which may take a short amount of time. + +### Development + +You can start the dev process with: + +```bash +npm run dev +``` + +This will automatically update the `index.js` and `index.css` of your plugin as soon as you make changes. +Reload the Panel to see your code changes reflected. + +With kirbyup 2.0.0+ and Kirby 3.7.4+ you can alternatively use hot module reloading (HMR): + +```bash +npm run serve +``` + +This will start a development server that updates the page as soon as you make changes. Some updates are instant, like CSS or Vue template changes, others require a reload of the page, which happens automatically. + +> [!NOTE] +> The live reload functionality requires top level await, [which is only supported in modern browsers](https://caniuse.com/mdn-javascript_operators_await_top_level). If you're developing in older browsers, use `npm run dev` and reload the page manually to see changes. + +### Production + +As soon as you are happy with your plugin, you should build the final version with: + +```bash +npm run build +``` + +This will automatically create a minified and optimized version of your `index.js` and `index.css` +which you can ship with your plugin. + +We have a tutorial on how to build your own plugin based on the Pluginkit [in the Kirby documentation](https://getkirby.com/docs/guide/plugins/plugin-setup-basic). + +### Build reproducibility + +While kirbyup will stay backwards compatible, exact build reproducibility may be of importance to you. If so, we recommend to target a specific package version, rather than using npx: + +```json +{ + "scripts": { + "dev": "kirbyup src/index.js --watch", + "build": "kirbyup src/index.js" + }, + "devDependencies": { + "kirbyup": "^3.1.0" + } +} +``` + +What follows is an example README for your plugin. + +**** + +## Installation + +### Download + +Download and copy this repository to `/site/plugins/{{ plugin-name }}`. + +### Git submodule + +```bash +git submodule add https://github.com/{{ your-name }}/{{ plugin-name }}.git site/plugins/{{ plugin-name }} +``` + +### Composer + +```bash +composer require {{ your-name }}/{{ plugin-name }} +``` + +## Setup + +*Additional instructions on how to configure the plugin (e.g. blueprint setup, config options, etc.)* + +## Options + +*Document the options and APIs that this plugin offers* + +## Development + +*Add instructions on how to help working on the plugin (e.g. npm setup, Composer dev dependencies, etc.)* + +## License + +MIT + +## Credits + +- [Your Name](https://github.com/ghost) diff --git a/site/plugins/shopify-refresh-button/SECURITY.md b/site/plugins/shopify-refresh-button/SECURITY.md new file mode 100644 index 0000000..3726336 --- /dev/null +++ b/site/plugins/shopify-refresh-button/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions + +*Use this section to tell people about which versions of your project are currently being supported with security updates.* + +| Version | Supported | +| ------- | ------------------ | +| 5.1.x | :white_check_mark: | +| 5.0.x | :x: | +| 4.0.x | :white_check_mark: | +| < 4.0 | :x: | + +## Reporting a Vulnerability + +*Use this section to tell people how to report a vulnerability.* + +*Tell them where to go, how often they can expect to get an update on a reported vulnerability, what to expect if the vulnerability is accepted or declined, etc.* diff --git a/site/plugins/shopify-refresh-button/composer.json b/site/plugins/shopify-refresh-button/composer.json new file mode 100755 index 0000000..fa07b14 --- /dev/null +++ b/site/plugins/shopify-refresh-button/composer.json @@ -0,0 +1,21 @@ +{ + "name": "getkirby/pluginkit", + "description": "Kirby Example Plugin", + "license": "MIT", + "type": "kirby-plugin", + "version": "1.0.0", + "authors": [ + { + "name": "Your Name", + "email": "you@example.com" + } + ], + "require": { + "getkirby/composer-installer": "^1.1" + }, + "config": { + "allow-plugins": { + "getkirby/composer-installer": true + } + } +} diff --git a/site/plugins/shopify-refresh-button/composer.lock b/site/plugins/shopify-refresh-button/composer.lock new file mode 100644 index 0000000..a5ae0fa --- /dev/null +++ b/site/plugins/shopify-refresh-button/composer.lock @@ -0,0 +1,66 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "37a8e61308b9b6f49cb9835f477f0c64", + "packages": [ + { + "name": "getkirby/composer-installer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/getkirby/composer-installer.git", + "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getkirby/composer-installer/zipball/c98ece30bfba45be7ce457e1102d1b169d922f3d", + "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^1.8 || ^2.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Kirby\\ComposerInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "Kirby\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Kirby's custom Composer installer for the Kirby CMS and for Kirby plugins", + "homepage": "https://getkirby.com", + "support": { + "issues": "https://github.com/getkirby/composer-installer/issues", + "source": "https://github.com/getkirby/composer-installer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://getkirby.com/buy", + "type": "custom" + } + ], + "time": "2020-12-28T12:54:39+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/site/plugins/shopify-refresh-button/index.js b/site/plugins/shopify-refresh-button/index.js new file mode 100644 index 0000000..677cea5 --- /dev/null +++ b/site/plugins/shopify-refresh-button/index.js @@ -0,0 +1,2 @@ +(function(){"use strict";function l(n,r,t,e,o,a,s,f){var i=typeof n=="function"?n.options:n;return r&&(i.render=r,i.staticRenderFns=t,i._compiled=!0),{exports:n,options:i}}const p={__name:"ShopifyRefreshButton",props:{products:{type:Array,default:()=>[]}},setup(n){const r=n,t=Vue.ref("Synchroniser depuis Shopify"),e=Vue.ref("refresh"),o=Vue.ref("aqua-icon"),a=Vue.ref(!1),s=Vue.ref([]);Vue.onMounted(()=>{s.value=r.products||[]});const f=Vue.computed(()=>{const u=s.value.length;return`${u} produit${u>1?"s":""} Shopify en cache`}),i=Vue.computed(()=>s.value.length===0?"Aucun produit trouvé. Cliquez sur 'Rafraîchir Shopify' pour récupérer les produits.":s.value.map(u=>`• ${u.title}
`).join(` +`));async function y(){a.value=!0,e.value="loader",o.value="orange-icon",t.value="En cours…";try{const c=await(await fetch("/shopify/refresh-cache.json",{method:"POST",headers:{"Content-Type":"application/json"}})).json();if(c.status==="error")throw new Error(c.message);s.value=c.products||[],t.value=`Terminé - ${c.count} produit${c.count>1?"s":""}`,e.value="check",o.value="green-icon",setTimeout(()=>{t.value="Synchroniser depuis Shopify",e.value="refresh",o.value="aqua-icon",a.value=!1},3e3)}catch(u){console.error(u),t.value="Erreur",e.value="alert",o.value="red-icon",setTimeout(()=>{t.value="Synchroniser depuis Shopify",e.value="refresh",o.value="aqua-icon",a.value=!1},3e3)}}return{__sfc:!0,props:r,text:t,icon:e,theme:o,isProcessing:a,currentProducts:s,infoLabel:f,infoText:i,refreshCache:y}}};var h=function(){var r=this,t=r._self._c,e=r._self._setupProxy;return t("div",[t("k-info-field",{attrs:{label:e.infoLabel,text:e.infoText,theme:"info"}}),t("k-button",{staticStyle:{"margin-top":"1rem"},attrs:{theme:e.theme,variant:"dimmed",icon:e.icon,title:"Synchroniser le cache des produits Shopify",disabled:e.isProcessing},on:{click:function(o){return e.refreshCache()}}},[r._v(" "+r._s(e.text)+" ")])],1)},d=[],v=l(p,h,d);const m=v.exports;window.panel.plugin("index/shopify-refresh-button",{fields:{"shopify-refresh":m}})})(); diff --git a/site/plugins/shopify-refresh-button/index.php b/site/plugins/shopify-refresh-button/index.php new file mode 100755 index 0000000..2fd9cdf --- /dev/null +++ b/site/plugins/shopify-refresh-button/index.php @@ -0,0 +1,46 @@ + [ + 'shopify-refresh' => [ + 'props' => [ + 'products' => function() { + return getShopifyProducts(); + } + ], + ] + ], + 'routes' => [ + [ + 'pattern' => 'shopify/refresh-cache.json', + 'method' => 'POST', + 'action' => function() { + if (!kirby()->user()) { + return [ + 'status' => 'error', + 'message' => 'Unauthorized' + ]; + } + + try { + kirby()->cache('shopify')->flush(); + + $products = fetchShopifyProducts(); + + return [ + 'status' => 'success', + 'message' => 'Cache Shopify rafraîchi avec succès', + 'count' => count($products), + 'products' => $products + ]; + } catch (\Throwable $e) { + return [ + 'status' => 'error', + 'message' => 'Erreur lors du rafraîchissement du cache', + 'details' => $e->getMessage() + ]; + } + } + ] + ] +]); diff --git a/site/plugins/shopify-refresh-button/package.json b/site/plugins/shopify-refresh-button/package.json new file mode 100644 index 0000000..bdbe47f --- /dev/null +++ b/site/plugins/shopify-refresh-button/package.json @@ -0,0 +1,7 @@ +{ + "scripts": { + "dev": "npx -y kirbyup src/index.js --watch", + "serve": "npx -y kirbyup serve src/index.js", + "build": "npx -y kirbyup src/index.js" + } +} diff --git a/site/plugins/shopify-refresh-button/src/components/ShopifyRefreshButton.vue b/site/plugins/shopify-refresh-button/src/components/ShopifyRefreshButton.vue new file mode 100644 index 0000000..83425e4 --- /dev/null +++ b/site/plugins/shopify-refresh-button/src/components/ShopifyRefreshButton.vue @@ -0,0 +1,97 @@ + + + diff --git a/site/plugins/shopify-refresh-button/src/index.js b/site/plugins/shopify-refresh-button/src/index.js new file mode 100755 index 0000000..7fedea6 --- /dev/null +++ b/site/plugins/shopify-refresh-button/src/index.js @@ -0,0 +1,7 @@ +import ShopifyRefreshButton from "./components/ShopifyRefreshButton.vue"; + +window.panel.plugin("index/shopify-refresh-button", { + fields: { + "shopify-refresh": ShopifyRefreshButton + } +});