Skip to content

Commit aaddafd

Browse files
authored
Merge pull request #243 from gadget-inc/riley/product-tagger-1-7
update product tagger template to v1.7, polaris web components, react router
2 parents 666ed80 + 1c04f4d commit aaddafd

51 files changed

Lines changed: 3623 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.gadget/
2+
node_modules/
3+
**/.DS_Store
4+
.react-router/
5+
6+
# Shopify
7+
extensions/**/dist
8+
extensions/**/node_modules
9+
extensions/**/generated
10+
.env.*
11+
.shopify/
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Product Tagger
2+
3+
Product Tagger lets merchants define approved keywords and automatically applies them as Shopify product tags when those keywords appear in a product's description.
4+
5+
## Setup
6+
7+
1. Connect the app to Shopifand install it on a development store in Gadget: **Settings** > **Plugins** > **Shopify** > **Connect a Shopify app**
8+
2. Open the embedded app in Shopify Admin and add one or more allowed keywords.
9+
3. Create or update a Shopify product whose description includes one of those keywords.
10+
4. After the Shopify product webhook runs, the matching keywords should appear on the product as tags.
11+
12+
## How it works
13+
14+
- Merchants manage approved keywords in the embedded app UI.
15+
- Keywords are stored per shop using the `allowedTag` model.
16+
- Shopify product create and update actions call [`api/models/shopifyProduct/utils.ts`](/Users/gadget/Projects/templates/shopify/product-tagger-public-template/api/models/shopifyProduct/utils.ts).
17+
- The helper strips HTML from the product description, extracts candidate words, and queries only matching `allowedTag` records for the current shop.
18+
- Matching keywords are merged with the product's existing tags and written back to Shopify with `api.enqueue(shopify.graphql, ...)` so the write can be retried safely in the background.
19+
20+
This keeps the app's behavior simple while avoiding a full scan of every allowed tag on each Shopify product webhook.
21+
22+
## Key files
23+
24+
- [`shopify.app.toml`](/Users/gadget/Projects/templates/shopify/product-tagger-public-template/shopify.app.toml): Shopify app configuration and scopes
25+
- [`web/routes/_app._index.tsx`](/Users/gadget/Projects/templates/shopify/product-tagger-public-template/web/routes/_app._index.tsx): Embedded app UI for managing allowed tags
26+
- [`api/models/allowedTag/schema.gadget.ts`](/Users/gadget/Projects/templates/shopify/product-tagger-public-template/api/models/allowedTag/schema.gadget.ts): Merchant-defined keyword model
27+
- [`api/models/shopifyProduct/actions/create.ts`](/Users/gadget/Projects/templates/shopify/product-tagger-public-template/api/models/shopifyProduct/actions/create.ts): Trigger for new Shopify products
28+
- [`api/models/shopifyProduct/actions/update.ts`](/Users/gadget/Projects/templates/shopify/product-tagger-public-template/api/models/shopifyProduct/actions/update.ts): Trigger for product description changes
29+
- [`api/models/shopifyProduct/utils.ts`](/Users/gadget/Projects/templates/shopify/product-tagger-public-template/api/models/shopifyProduct/utils.ts): Keyword matching and Shopify tag application logic
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
filter ($session: Session) on AllowedTag [
2+
where shopId == $session.shopId
3+
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
filter ($session: Session) on ShopifyGdprRequest [
2+
where shopId == $session.shopId
3+
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
filter ($session: Session) on ShopifyProduct [
2+
where shopId == $session.shopId
3+
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
filter ($session: Session) on ShopifyShop [
2+
where id == $session.shopId
3+
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
filter ($session: Session) on ShopifySync [
2+
where shopId == $session.shopId
3+
]
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import type { GadgetPermissions } from "gadget-server";
2+
3+
/**
4+
* This metadata describes the access control configuration available in your application.
5+
* Grants that are not defined here are set to false by default.
6+
*
7+
* View and edit your roles and permissions in the Gadget editor at https://product-tagger-public-template.gadget.app/edit/settings/permissions
8+
*/
9+
export const permissions: GadgetPermissions = {
10+
type: "gadget/permissions/v1",
11+
roles: {
12+
"shopify-app-users": {
13+
storageKey: "Role-Shopify-App",
14+
models: {
15+
allowedTag: {
16+
read: {
17+
filter:
18+
"accessControl/filters/allowedTag/allowedTag.gelly",
19+
},
20+
actions: {
21+
create: true,
22+
delete: true,
23+
},
24+
},
25+
shopifyGdprRequest: {
26+
read: {
27+
filter:
28+
"accessControl/filters/shopify/shopifyGdprRequest.gelly",
29+
},
30+
actions: {
31+
create: true,
32+
update: true,
33+
},
34+
},
35+
shopifyProduct: {
36+
read: {
37+
filter:
38+
"accessControl/filters/shopify/shopifyProduct.gelly",
39+
},
40+
actions: {
41+
create: true,
42+
delete: true,
43+
update: true,
44+
},
45+
},
46+
shopifyShop: {
47+
read: {
48+
filter: "accessControl/filters/shopify/shopifyShop.gelly",
49+
},
50+
actions: {
51+
install: true,
52+
reinstall: true,
53+
uninstall: true,
54+
update: true,
55+
},
56+
},
57+
shopifySync: {
58+
read: {
59+
filter: "accessControl/filters/shopify/shopifySync.gelly",
60+
},
61+
actions: {
62+
abort: true,
63+
complete: true,
64+
error: true,
65+
run: true,
66+
},
67+
},
68+
},
69+
},
70+
unauthenticated: {
71+
storageKey: "unauthenticated",
72+
},
73+
},
74+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { applyParams, save, ActionOptions, preventCrossShopDataAccess } from "gadget-server";
2+
3+
export const run: ActionRun = async ({ params, record, logger, api, connections }) => {
4+
applyParams(params, record);
5+
await preventCrossShopDataAccess(params, record);
6+
await save(record);
7+
};
8+
9+
export const options: ActionOptions = {
10+
actionType: "create",
11+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { deleteRecord, ActionOptions, preventCrossShopDataAccess } from "gadget-server";
2+
3+
export const run: ActionRun = async ({ params, record, logger, api, connections }) => {
4+
await preventCrossShopDataAccess(params, record);
5+
await deleteRecord(record);
6+
};
7+
8+
export const options: ActionOptions = {
9+
actionType: "delete",
10+
};

0 commit comments

Comments
 (0)