astro-tractstack 2.2.10 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/create-tractstack.js +2 -2
- package/dist/index.js +177 -18
- package/package.json +4 -2
- package/templates/custom/minimal/CodeHook.astro +22 -5
- package/templates/custom/shopify/Cart.tsx +372 -0
- package/templates/custom/shopify/CartIcon.tsx +47 -0
- package/templates/custom/shopify/CartModal.tsx +63 -0
- package/templates/custom/shopify/CheckoutModal.tsx +576 -0
- package/templates/custom/shopify/NativeBookingCalendar.tsx +375 -0
- package/templates/custom/shopify/ShopifyCartManager.tsx +200 -0
- package/templates/custom/shopify/ShopifyCheckout.tsx +167 -0
- package/templates/custom/shopify/ShopifyProductGrid.tsx +247 -0
- package/templates/custom/shopify/ShopifyServiceList.tsx +135 -0
- package/templates/custom/shopify/cart.astro +23 -0
- package/templates/custom/with-examples/CodeHook.astro +17 -1
- package/templates/custom/with-examples/ProductGrid.astro +1 -1
- package/templates/src/client/app.js +4 -2
- package/templates/src/components/Footer.astro +4 -4
- package/templates/src/components/Header.astro +44 -12
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +3 -3
- package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +2 -2
- package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +2 -2
- package/templates/src/components/edit/pane/steps/AiLibraryCopyStep.tsx +3 -3
- package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +2 -2
- package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +7 -7
- package/templates/src/components/form/advanced/APIConfigSection.tsx +407 -38
- package/templates/src/components/form/shopify/SchedulingSection.tsx +354 -0
- package/templates/src/components/storykeep/Dashboard.tsx +18 -4
- package/templates/src/components/storykeep/Dashboard_Advanced.tsx +1 -0
- package/templates/src/components/storykeep/Dashboard_Content.tsx +5 -96
- package/templates/src/components/storykeep/Dashboard_Shopify.tsx +668 -0
- package/templates/src/components/storykeep/StoryKeepBackdrop.astro +43 -23
- package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +14 -5
- package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +0 -14
- package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +36 -13
- package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +5 -2
- package/templates/src/components/storykeep/controls/content/ManageContent.tsx +4 -11
- package/templates/src/components/storykeep/controls/content/MenuTable.tsx +14 -5
- package/templates/src/components/storykeep/controls/content/ProductTable.tsx +333 -0
- package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +9 -5
- package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +108 -8
- package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +13 -4
- package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +14 -5
- package/templates/src/components/storykeep/shopify/ShopifyDashboard.tsx +111 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx +393 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Products.tsx +46 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Schedule.tsx +78 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Search.tsx +55 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Services.tsx +47 -0
- package/templates/src/lib/resources.ts +11 -21
- package/templates/src/pages/api/auth/lookup-lead.ts +72 -0
- package/templates/src/pages/api/booking/availability.ts +72 -0
- package/templates/src/pages/api/booking/cancel.ts +73 -0
- package/templates/src/pages/api/booking/confirm.ts +82 -0
- package/templates/src/pages/api/booking/hold.ts +75 -0
- package/templates/src/pages/api/booking/list.ts +66 -0
- package/templates/src/pages/api/booking/metrics.ts +60 -0
- package/templates/src/pages/api/booking/release.ts +76 -0
- package/templates/src/pages/api/sandbox.ts +2 -2
- package/templates/src/pages/api/shopify/createCart.ts +69 -0
- package/templates/src/pages/api/shopify/getProducts.ts +64 -0
- package/templates/src/pages/storykeep/login.astro +26 -24
- package/templates/src/pages/storykeep/logout.astro +1 -10
- package/templates/src/pages/storykeep/manage.astro +69 -0
- package/templates/src/pages/storykeep/{content.astro → pages.astro} +4 -8
- package/templates/src/pages/storykeep/shopify.astro +101 -0
- package/templates/src/stores/navigation.ts +3 -42
- package/templates/src/stores/nodes.ts +3 -1
- package/templates/src/stores/resources.ts +7 -10
- package/templates/src/stores/shopify.ts +266 -0
- package/templates/src/types/tractstack.ts +75 -0
- package/templates/src/utils/api/advancedConfig.ts +7 -1
- package/templates/src/utils/api/advancedHelpers.ts +87 -7
- package/templates/src/utils/api/bookingHelpers.ts +125 -0
- package/templates/src/utils/api/brandHelpers.ts +14 -0
- package/templates/src/utils/api/resourceConfig.ts +13 -5
- package/templates/src/utils/auth.ts +29 -9
- package/templates/src/utils/compositor/aiGeneration.ts +3 -3
- package/templates/src/utils/compositor/aiPaneParser.ts +2 -2
- package/templates/src/utils/customHelpers.ts +49 -0
- package/templates/src/utils/helpers.ts +59 -0
- package/templates/src/utils/profileStorage.ts +5 -0
- package/templates/src/utils/tenantResolver.ts +2 -1
- package/utils/inject-files.ts +161 -2
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Dialog, Portal } from '@ark-ui/react';
|
|
1
3
|
import StringInput from '../StringInput';
|
|
4
|
+
import BooleanToggle from '../BooleanToggle';
|
|
2
5
|
import type { FormStateReturn } from '@/hooks/useFormState';
|
|
3
6
|
import type {
|
|
4
7
|
AdvancedConfigState,
|
|
@@ -16,54 +19,420 @@ export default function APIConfigSection({
|
|
|
16
19
|
}: APIConfigSectionProps) {
|
|
17
20
|
const { state, updateField, errors } = formState;
|
|
18
21
|
|
|
22
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
23
|
+
const goBackend =
|
|
24
|
+
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
25
|
+
|
|
26
|
+
// Status flags
|
|
19
27
|
const aaiConfigured = status?.aaiAPIKeySet;
|
|
28
|
+
const shopifyStorefrontConfigured = status?.shopifyStorefrontTokenSet;
|
|
29
|
+
const shopifySecretConfigured = status?.shopifyApiSecretSet;
|
|
30
|
+
const shopifyDomainConfigured = status?.shopifyStoreDomainSet;
|
|
31
|
+
const shopifyVersionConfigured = Boolean(status?.shopifyApiVersion);
|
|
32
|
+
const shopifyAdminSlugConfigured = status?.shopifyAdminSlugSet;
|
|
33
|
+
const shopifyWebhooksConfigured = status?.userSetupWebhooks;
|
|
34
|
+
const resendConfigured = status?.resendApiKeySet;
|
|
35
|
+
|
|
36
|
+
const renderStatusBadge = (isConfigured: boolean | undefined) => {
|
|
37
|
+
if (status === null) {
|
|
38
|
+
return (
|
|
39
|
+
<span className="ml-2 inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-bold text-gray-800">
|
|
40
|
+
Loading...
|
|
41
|
+
</span>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return isConfigured ? (
|
|
45
|
+
<span className="ml-2 inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-xs font-bold text-green-800">
|
|
46
|
+
✓ Set
|
|
47
|
+
</span>
|
|
48
|
+
) : (
|
|
49
|
+
<span className="ml-2 inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-bold text-yellow-800">
|
|
50
|
+
Not Set
|
|
51
|
+
</span>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
20
54
|
|
|
21
55
|
return (
|
|
22
56
|
<div className="bg-white shadow md:rounded-lg">
|
|
23
57
|
<div className="px-4 py-5 md:p-6">
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</h3>
|
|
28
|
-
<div className="flex items-center">
|
|
29
|
-
{aaiConfigured ? (
|
|
30
|
-
<span className="inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-bold text-green-800">
|
|
31
|
-
✓ Configured
|
|
32
|
-
</span>
|
|
33
|
-
) : status !== null ? (
|
|
34
|
-
<span className="inline-flex items-center rounded-full bg-yellow-100 px-2.5 py-0.5 text-xs font-bold text-yellow-800">
|
|
35
|
-
Optional
|
|
36
|
-
</span>
|
|
37
|
-
) : (
|
|
38
|
-
<span className="inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-bold text-gray-800">
|
|
39
|
-
Loading...
|
|
40
|
-
</span>
|
|
41
|
-
)}
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
58
|
+
<h3 className="text-base font-bold leading-6 text-gray-900">
|
|
59
|
+
API Integrations
|
|
60
|
+
</h3>
|
|
44
61
|
<div className="mt-2 max-w-xl text-sm text-gray-500">
|
|
45
|
-
<p>Configure external API keys for enhanced functionality.</p>
|
|
46
|
-
</div>
|
|
47
|
-
<div className="mt-5">
|
|
48
|
-
<StringInput
|
|
49
|
-
label="AssemblyAI API Key"
|
|
50
|
-
value={state.aaiApiKey}
|
|
51
|
-
onChange={(value) => updateField('aaiApiKey', value)}
|
|
52
|
-
type="password"
|
|
53
|
-
placeholder={
|
|
54
|
-
aaiConfigured ? '••••••••••••••••' : 'Enter AssemblyAI API key'
|
|
55
|
-
}
|
|
56
|
-
error={errors.aaiApiKey}
|
|
57
|
-
/>
|
|
58
|
-
</div>
|
|
59
|
-
<div className="mt-3 text-xs text-gray-500">
|
|
60
62
|
<p>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
{aaiConfigured && ' Leave blank to keep existing key.'}
|
|
63
|
+
Configure external services to enable advanced features like AI,
|
|
64
|
+
Commerce, and Email.
|
|
64
65
|
</p>
|
|
65
66
|
</div>
|
|
67
|
+
|
|
68
|
+
<div className="mt-6 space-y-8">
|
|
69
|
+
{/* AssemblyAI Section */}
|
|
70
|
+
<div className="border-t border-gray-100 pt-6">
|
|
71
|
+
<div className="mb-4 flex items-center justify-between">
|
|
72
|
+
<h4 className="text-sm font-bold text-gray-900">
|
|
73
|
+
AssemblyAI (Audio Intelligence)
|
|
74
|
+
</h4>
|
|
75
|
+
{renderStatusBadge(aaiConfigured)}
|
|
76
|
+
</div>
|
|
77
|
+
<StringInput
|
|
78
|
+
label="API Key"
|
|
79
|
+
value={state.aaiApiKey}
|
|
80
|
+
onChange={(value) => updateField('aaiApiKey', value)}
|
|
81
|
+
type="password"
|
|
82
|
+
placeholder={
|
|
83
|
+
aaiConfigured ? '••••••••••••••••' : 'Enter AssemblyAI API key'
|
|
84
|
+
}
|
|
85
|
+
error={errors.aaiApiKey}
|
|
86
|
+
/>
|
|
87
|
+
<p className="mt-2 text-xs text-gray-500">
|
|
88
|
+
Required for audio transcription and analysis.
|
|
89
|
+
{aaiConfigured && ' Leave blank to keep existing key.'}
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
{/* Shopify Section */}
|
|
94
|
+
<div className="border-t border-gray-100 pt-6">
|
|
95
|
+
<div className="mb-4 flex items-center justify-between">
|
|
96
|
+
<h4 className="text-sm font-bold text-gray-900">
|
|
97
|
+
Shopify (Commerce)
|
|
98
|
+
</h4>
|
|
99
|
+
{renderStatusBadge(
|
|
100
|
+
shopifyStorefrontConfigured &&
|
|
101
|
+
shopifySecretConfigured &&
|
|
102
|
+
shopifyDomainConfigured &&
|
|
103
|
+
shopifyVersionConfigured &&
|
|
104
|
+
shopifyAdminSlugConfigured &&
|
|
105
|
+
shopifyWebhooksConfigured
|
|
106
|
+
)}
|
|
107
|
+
</div>
|
|
108
|
+
<div className="space-y-4">
|
|
109
|
+
<div>
|
|
110
|
+
<StringInput
|
|
111
|
+
label="Shopify Store Domain"
|
|
112
|
+
value={state.shopifyStoreDomain}
|
|
113
|
+
onChange={(value) => updateField('shopifyStoreDomain', value)}
|
|
114
|
+
placeholder={
|
|
115
|
+
shopifyDomainConfigured
|
|
116
|
+
? 'your-shop.myshopify.com'
|
|
117
|
+
: 'your-shop.myshopify.com'
|
|
118
|
+
}
|
|
119
|
+
error={errors.shopifyStoreDomain}
|
|
120
|
+
/>
|
|
121
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
122
|
+
The primary .myshopify.com domain for your store.
|
|
123
|
+
</p>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div>
|
|
127
|
+
<StringInput
|
|
128
|
+
label="Shopify Admin Slug"
|
|
129
|
+
value={state.shopifyAdminSlug}
|
|
130
|
+
onChange={(value) => updateField('shopifyAdminSlug', value)}
|
|
131
|
+
placeholder="your-store-slug"
|
|
132
|
+
error={errors.shopifyAdminSlug}
|
|
133
|
+
/>
|
|
134
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
135
|
+
The internal Shopify slug (found in
|
|
136
|
+
admin.shopify.com/store/SLUG).
|
|
137
|
+
</p>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<div>
|
|
141
|
+
<StringInput
|
|
142
|
+
label="API Version"
|
|
143
|
+
value={state.shopifyApiVersion}
|
|
144
|
+
onChange={(value) => updateField('shopifyApiVersion', value)}
|
|
145
|
+
type="text"
|
|
146
|
+
placeholder="2026-01"
|
|
147
|
+
error={errors.shopifyApiVersion}
|
|
148
|
+
/>
|
|
149
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
150
|
+
Target specific Shopify API version (YYYY-MM).
|
|
151
|
+
</p>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div>
|
|
155
|
+
<StringInput
|
|
156
|
+
label="Headless channel, Private Access Token"
|
|
157
|
+
value={state.shopifyStorefrontToken}
|
|
158
|
+
onChange={(value) =>
|
|
159
|
+
updateField('shopifyStorefrontToken', value)
|
|
160
|
+
}
|
|
161
|
+
type="password"
|
|
162
|
+
placeholder={
|
|
163
|
+
shopifyStorefrontConfigured
|
|
164
|
+
? '••••••••••••••••'
|
|
165
|
+
: 'shpat_...'
|
|
166
|
+
}
|
|
167
|
+
error={errors.shopifyStorefrontToken}
|
|
168
|
+
/>
|
|
169
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
170
|
+
Private access token for fetching products.
|
|
171
|
+
{shopifyStorefrontConfigured &&
|
|
172
|
+
' Leave blank to keep existing.'}
|
|
173
|
+
</p>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div>
|
|
177
|
+
<StringInput
|
|
178
|
+
label="API Secret Key"
|
|
179
|
+
value={state.shopifyApiSecret}
|
|
180
|
+
onChange={(value) => updateField('shopifyApiSecret', value)}
|
|
181
|
+
type="password"
|
|
182
|
+
placeholder={
|
|
183
|
+
shopifySecretConfigured ? '••••••••••••••••' : 'shpss_...'
|
|
184
|
+
}
|
|
185
|
+
error={errors.shopifyApiSecret}
|
|
186
|
+
/>
|
|
187
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
188
|
+
Required for Webhook signature verification.
|
|
189
|
+
{shopifySecretConfigured && ' Leave blank to keep existing.'}
|
|
190
|
+
</p>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
{!shopifyWebhooksConfigured && (
|
|
194
|
+
<div className="mt-6 border-t border-gray-100 pt-6">
|
|
195
|
+
<div className="mb-4 rounded-md border border-amber-200 bg-amber-50 p-4">
|
|
196
|
+
<h5 className="text-sm font-bold text-amber-800">
|
|
197
|
+
Webhook Configuration Required
|
|
198
|
+
</h5>
|
|
199
|
+
<p className="mb-3 mt-1 text-xs text-amber-700">
|
|
200
|
+
To ensure synchronization between Shopify and the
|
|
201
|
+
TractStack native booking system, you must configure
|
|
202
|
+
webhook subscriptions within your Shopify Admin panel.
|
|
203
|
+
</p>
|
|
204
|
+
<button
|
|
205
|
+
onClick={() => setIsModalOpen(true)}
|
|
206
|
+
className="text-xs font-bold text-amber-900 underline hover:text-amber-700"
|
|
207
|
+
type="button"
|
|
208
|
+
>
|
|
209
|
+
[ Detailed Instructions ]
|
|
210
|
+
</button>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<BooleanToggle
|
|
214
|
+
label="Webhooks Manually Configured"
|
|
215
|
+
description="I have manually created the required webhooks (orders/paid, products/*) in my Shopify Admin."
|
|
216
|
+
value={state.userSetupWebhooks}
|
|
217
|
+
onChange={(value) =>
|
|
218
|
+
updateField('userSetupWebhooks', value)
|
|
219
|
+
}
|
|
220
|
+
/>
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{/* Resend Section */}
|
|
227
|
+
<div className="border-t border-gray-100 pt-6">
|
|
228
|
+
<div className="mb-4 flex items-center justify-between">
|
|
229
|
+
<h4 className="text-sm font-bold text-gray-900">
|
|
230
|
+
Resend (Transactional Email)
|
|
231
|
+
</h4>
|
|
232
|
+
{renderStatusBadge(resendConfigured)}
|
|
233
|
+
</div>
|
|
234
|
+
<StringInput
|
|
235
|
+
label="API Key"
|
|
236
|
+
value={state.resendApiKey}
|
|
237
|
+
onChange={(value) => updateField('resendApiKey', value)}
|
|
238
|
+
type="password"
|
|
239
|
+
placeholder={resendConfigured ? '••••••••••••••••' : 're_...'}
|
|
240
|
+
error={errors.resendApiKey}
|
|
241
|
+
/>
|
|
242
|
+
<p className="mt-2 text-xs text-gray-500">
|
|
243
|
+
Required for sending system emails.
|
|
244
|
+
{resendConfigured && ' Leave blank to keep existing key.'}
|
|
245
|
+
</p>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
66
248
|
</div>
|
|
249
|
+
|
|
250
|
+
<Dialog.Root
|
|
251
|
+
open={isModalOpen}
|
|
252
|
+
onOpenChange={(details) => setIsModalOpen(details.open)}
|
|
253
|
+
preventScroll={true}
|
|
254
|
+
lazyMount
|
|
255
|
+
unmountOnExit
|
|
256
|
+
>
|
|
257
|
+
<Portal>
|
|
258
|
+
<Dialog.Backdrop className="fixed inset-0 z-50 bg-black bg-opacity-75" />
|
|
259
|
+
<Dialog.Positioner className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
|
260
|
+
<Dialog.Content
|
|
261
|
+
className="relative flex w-full max-w-2xl flex-col overflow-hidden rounded-lg bg-white shadow-xl"
|
|
262
|
+
style={{ maxHeight: '90vh' }}
|
|
263
|
+
>
|
|
264
|
+
{/* Header - Fixed height */}
|
|
265
|
+
<div className="flex items-center justify-between border-b border-gray-200 px-6 py-4">
|
|
266
|
+
<Dialog.Title className="text-xl font-bold text-gray-900">
|
|
267
|
+
How to Configure Webhooks in Shopify
|
|
268
|
+
</Dialog.Title>
|
|
269
|
+
<Dialog.CloseTrigger asChild>
|
|
270
|
+
<button
|
|
271
|
+
className="text-gray-400 hover:text-gray-600"
|
|
272
|
+
type="button"
|
|
273
|
+
>
|
|
274
|
+
<span className="text-2xl">×</span>
|
|
275
|
+
</button>
|
|
276
|
+
</Dialog.CloseTrigger>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
{/* Body - Scrollable via flex-1 */}
|
|
280
|
+
<div className="flex-1 overflow-y-auto p-6 text-sm text-gray-700">
|
|
281
|
+
<ol className="mb-6 list-decimal space-y-2 pl-5">
|
|
282
|
+
<li>Log in to your Shopify Admin dashboard.</li>
|
|
283
|
+
<li>
|
|
284
|
+
Click on <strong>Settings</strong> (the gear icon) in the
|
|
285
|
+
bottom left corner.
|
|
286
|
+
</li>
|
|
287
|
+
<li>
|
|
288
|
+
In the left sidebar, select <strong>Notifications</strong>.
|
|
289
|
+
</li>
|
|
290
|
+
<li>
|
|
291
|
+
Scroll all the way to the bottom to the{' '}
|
|
292
|
+
<strong>Webhooks</strong> section.
|
|
293
|
+
</li>
|
|
294
|
+
<li>
|
|
295
|
+
Click the <strong>Create webhook</strong> button.
|
|
296
|
+
</li>
|
|
297
|
+
<li>
|
|
298
|
+
For each required webhook (listed below), configure the
|
|
299
|
+
following settings:
|
|
300
|
+
<ul className="mt-2 list-disc space-y-1 pl-5">
|
|
301
|
+
<li>
|
|
302
|
+
<strong>Event:</strong> Select the specific event (e.g.,
|
|
303
|
+
Order payment, Product creation).
|
|
304
|
+
</li>
|
|
305
|
+
<li>
|
|
306
|
+
<strong>Format:</strong> Select JSON (TractStack relies
|
|
307
|
+
on JSON unmarshaling).
|
|
308
|
+
</li>
|
|
309
|
+
<li>
|
|
310
|
+
<strong>URL:</strong> Enter your backend webhook URL:{' '}
|
|
311
|
+
<code className="rounded bg-gray-100 px-1 py-0.5 text-xs">
|
|
312
|
+
{goBackend}/api/v1/hooks/shopify
|
|
313
|
+
</code>
|
|
314
|
+
</li>
|
|
315
|
+
<li>
|
|
316
|
+
<strong>Webhook API version:</strong> Select the version
|
|
317
|
+
that matches your ShopifyAPIVersion configured in
|
|
318
|
+
TractStack.
|
|
319
|
+
</li>
|
|
320
|
+
</ul>
|
|
321
|
+
</li>
|
|
322
|
+
<li>
|
|
323
|
+
Click <strong>Save</strong>.
|
|
324
|
+
</li>
|
|
325
|
+
</ol>
|
|
326
|
+
|
|
327
|
+
<div className="mb-6 border-l-4 border-red-500 bg-red-50 p-4">
|
|
328
|
+
<p className="font-bold text-red-800">CRITICAL:</p>
|
|
329
|
+
<p className="mt-1 text-red-700">
|
|
330
|
+
After saving your first webhook, Shopify will display a{' '}
|
|
331
|
+
<strong>Webhook signing secret</strong> at the bottom of the
|
|
332
|
+
Webhooks section. You must copy this secret and add it to
|
|
333
|
+
your TractStack API Config as the Shopify API Secret. The
|
|
334
|
+
backend uses this to verify the HMAC signature of all
|
|
335
|
+
incoming payloads.
|
|
336
|
+
</p>
|
|
337
|
+
</div>
|
|
338
|
+
|
|
339
|
+
<h4 className="mb-3 text-lg font-bold">
|
|
340
|
+
Required Webhooks Breakdown
|
|
341
|
+
</h4>
|
|
342
|
+
<p className="mb-4">
|
|
343
|
+
You must create a separate webhook subscription for each of
|
|
344
|
+
the following four topics. All of them should point to the
|
|
345
|
+
exact same URL:{' '}
|
|
346
|
+
<code className="rounded bg-gray-100 px-1 py-0.5 text-xs">
|
|
347
|
+
{goBackend}/api/v1/hooks/shopify
|
|
348
|
+
</code>
|
|
349
|
+
</p>
|
|
350
|
+
|
|
351
|
+
<div className="space-y-4">
|
|
352
|
+
<div className="rounded border border-gray-200 bg-gray-50 p-4">
|
|
353
|
+
<h5 className="font-bold">1. Order Paid</h5>
|
|
354
|
+
<ul className="mt-2 space-y-1 text-xs">
|
|
355
|
+
<li>
|
|
356
|
+
<span className="font-bold text-gray-900">
|
|
357
|
+
Shopify Event Name:
|
|
358
|
+
</span>{' '}
|
|
359
|
+
Order payment
|
|
360
|
+
</li>
|
|
361
|
+
<li>
|
|
362
|
+
<span className="font-bold text-gray-900">
|
|
363
|
+
Topic Header:
|
|
364
|
+
</span>{' '}
|
|
365
|
+
orders/paid
|
|
366
|
+
</li>
|
|
367
|
+
<li>
|
|
368
|
+
<span className="font-bold text-gray-900">
|
|
369
|
+
Purpose:
|
|
370
|
+
</span>{' '}
|
|
371
|
+
Transitions the corresponding hold in the bookings
|
|
372
|
+
database table from PENDING to CONFIRMED.
|
|
373
|
+
</li>
|
|
374
|
+
</ul>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<div className="rounded border border-gray-200 bg-gray-50 p-4">
|
|
378
|
+
<h5 className="font-bold">2. Product Creation</h5>
|
|
379
|
+
<ul className="mt-2 space-y-1 text-xs">
|
|
380
|
+
<li>
|
|
381
|
+
<span className="font-bold text-gray-900">
|
|
382
|
+
Shopify Event Name:
|
|
383
|
+
</span>{' '}
|
|
384
|
+
Product creation
|
|
385
|
+
</li>
|
|
386
|
+
<li>
|
|
387
|
+
<span className="font-bold text-gray-900">
|
|
388
|
+
Topic Header:
|
|
389
|
+
</span>{' '}
|
|
390
|
+
products/create
|
|
391
|
+
</li>
|
|
392
|
+
</ul>
|
|
393
|
+
</div>
|
|
394
|
+
|
|
395
|
+
<div className="rounded border border-gray-200 bg-gray-50 p-4">
|
|
396
|
+
<h5 className="font-bold">3. Product Update</h5>
|
|
397
|
+
<ul className="mt-2 space-y-1 text-xs">
|
|
398
|
+
<li>
|
|
399
|
+
<span className="font-bold text-gray-900">
|
|
400
|
+
Shopify Event Name:
|
|
401
|
+
</span>{' '}
|
|
402
|
+
Product update
|
|
403
|
+
</li>
|
|
404
|
+
<li>
|
|
405
|
+
<span className="font-bold text-gray-900">
|
|
406
|
+
Topic Header:
|
|
407
|
+
</span>{' '}
|
|
408
|
+
products/update
|
|
409
|
+
</li>
|
|
410
|
+
</ul>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
<div className="rounded border border-gray-200 bg-gray-50 p-4">
|
|
414
|
+
<h5 className="font-bold">4. Product Deletion</h5>
|
|
415
|
+
<ul className="mt-2 space-y-1 text-xs">
|
|
416
|
+
<li>
|
|
417
|
+
<span className="font-bold text-gray-900">
|
|
418
|
+
Shopify Event Name:
|
|
419
|
+
</span>{' '}
|
|
420
|
+
Product deletion
|
|
421
|
+
</li>
|
|
422
|
+
<li>
|
|
423
|
+
<span className="font-bold text-gray-900">
|
|
424
|
+
Topic Header:
|
|
425
|
+
</span>{' '}
|
|
426
|
+
products/delete
|
|
427
|
+
</li>
|
|
428
|
+
</ul>
|
|
429
|
+
</div>
|
|
430
|
+
</div>
|
|
431
|
+
</div>
|
|
432
|
+
</Dialog.Content>
|
|
433
|
+
</Dialog.Positioner>
|
|
434
|
+
</Portal>
|
|
435
|
+
</Dialog.Root>
|
|
67
436
|
</div>
|
|
68
437
|
);
|
|
69
438
|
}
|