astro-tractstack 2.3.0 → 2.3.2
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 +130 -19
- package/package.json +2 -2
- package/templates/custom/minimal/CodeHook.astro +10 -2
- package/templates/custom/shopify/Cart.tsx +115 -77
- package/templates/custom/shopify/CheckoutModal.tsx +509 -120
- package/templates/custom/shopify/NativeBookingCalendar.tsx +375 -0
- package/templates/custom/shopify/ShopifyCartManager.tsx +91 -45
- package/templates/custom/shopify/ShopifyCheckout.tsx +4 -33
- package/templates/custom/shopify/ShopifyProductGrid.tsx +170 -176
- package/templates/custom/shopify/ShopifyServiceList.tsx +112 -51
- package/templates/custom/with-examples/CodeHook.astro +10 -2
- package/templates/src/components/Footer.astro +6 -6
- package/templates/src/components/Header.astro +23 -11
- package/templates/src/components/Menu.tsx +157 -135
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +2 -2
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +27 -6
- package/templates/src/components/codehooks/EpinetTableView.tsx +153 -112
- package/templates/src/components/codehooks/EpinetWrapper.tsx +4 -1
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +8 -1
- package/templates/src/components/codehooks/ProductCardSetup.tsx +9 -1
- package/templates/src/components/codehooks/ProductGridSetup.tsx +9 -1
- package/templates/src/components/compositor/nodes/BgPaneWrapper.tsx +2 -1
- package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +1 -1
- package/templates/src/components/edit/ToolBar.tsx +2 -1
- package/templates/src/components/edit/context/ContextPaneConfig_slug.tsx +2 -2
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +13 -0
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +3 -3
- package/templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx +2 -2
- package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +2 -2
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -1
- 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/edit/state/SaveModal.tsx +1 -1
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +8 -3
- package/templates/src/components/form/DateTimeInput.tsx +10 -3
- package/templates/src/components/form/FileUpload.tsx +11 -5
- package/templates/src/components/form/NumberInput.tsx +2 -2
- package/templates/src/components/form/advanced/APIConfigSection.tsx +208 -2
- package/templates/src/components/form/brand/SiteConfigSection.tsx +10 -0
- package/templates/src/components/form/shopify/SchedulingSection.tsx +354 -0
- package/templates/src/components/storykeep/Dashboard.tsx +1 -1
- package/templates/src/components/storykeep/Dashboard_Shopify.tsx +252 -110
- package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +2 -2
- package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +14 -5
- package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +5 -2
- package/templates/src/components/storykeep/controls/content/MenuTable.tsx +14 -5
- package/templates/src/components/storykeep/controls/content/ProductTable.tsx +180 -101
- package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +88 -56
- package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +14 -4
- package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +14 -5
- package/templates/src/components/storykeep/email-builder/Blocks.tsx +169 -0
- package/templates/src/components/storykeep/email-builder/EmailBuilder.tsx +223 -0
- package/templates/src/components/storykeep/email-builder/PreviewModal.tsx +136 -0
- package/templates/src/components/storykeep/email-builder/PropertyPanel.tsx +154 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard.tsx +104 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx +419 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Emails.tsx +105 -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/layouts/Layout.astro +8 -5
- 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 +4 -8
- package/templates/src/pages/api/shopify/getProducts.ts +15 -15
- package/templates/src/pages/storykeep/login.astro +21 -14
- package/templates/src/stores/shopify.ts +97 -25
- package/templates/src/types/formTypes.ts +4 -2
- package/templates/src/types/tractstack.ts +59 -2
- package/templates/src/utils/api/advancedConfig.ts +2 -0
- package/templates/src/utils/api/advancedHelpers.ts +40 -3
- package/templates/src/utils/api/bookingHelpers.ts +125 -0
- package/templates/src/utils/api/brandConfig.ts +2 -0
- package/templates/src/utils/api/brandHelpers.ts +26 -0
- package/templates/src/utils/api/emailHelpers.ts +105 -0
- 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 +0 -21
- package/templates/src/utils/profileStorage.ts +5 -0
- package/templates/src/utils/tenantResolver.ts +3 -2
- package/utils/inject-files.ts +116 -5
- package/templates/custom/shopify/CalDotComBooking.tsx +0 -44
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ For more recipes and community guides, visit [FreeWebPress.org](https://FreeWebP
|
|
|
75
75
|
|
|
76
76
|
Expanding full-text search to include embedded video transcripts (via BunnyCDN).
|
|
77
77
|
|
|
78
|
-
- **AI Visual Editor:** Powered by AssemblyAI
|
|
78
|
+
- **AI Visual Editor:** Powered by AssemblyAI aai to highlight and extract transcript sections.
|
|
79
79
|
- **Smart Content:** Automated chaptering and descriptions for long-form video.
|
|
80
80
|
- **Video-to-Blog Pipeline:** Transform video highlights into rich, searchable blog pages using the design library.
|
|
81
81
|
|
package/bin/create-tractstack.js
CHANGED
|
@@ -348,7 +348,7 @@ PUBLIC_ENABLE_BUNNY="${finalResponses.enableBunny ? 'true' : 'false'}"
|
|
|
348
348
|
|
|
349
349
|
// Install UI components
|
|
350
350
|
execSync(
|
|
351
|
-
`${addCommand} @ark-ui/react@^5.30.0 @heroicons/react@^2.1.1 @internationalized/date@3.10.0
|
|
351
|
+
`${addCommand} @ark-ui/react@^5.30.0 @heroicons/react@^2.1.1 @internationalized/date@3.10.0`,
|
|
352
352
|
{
|
|
353
353
|
stdio: 'inherit',
|
|
354
354
|
}
|
|
@@ -382,7 +382,7 @@ PUBLIC_ENABLE_BUNNY="${finalResponses.enableBunny ? 'true' : 'false'}"
|
|
|
382
382
|
console.log('Please run manually:');
|
|
383
383
|
console.log(
|
|
384
384
|
kleur.cyan(
|
|
385
|
-
`${addCommand} react@^19.0.0 react-dom@^19.0.0 @astrojs/react@^4.4.2 @astrojs/node@^9.4.3 @nanostores/react@^1.0.0 nanostores@^1.0.1 @nanostores/persistent ulid@^3.0.1 @ark-ui/react@^5.30.0 @heroicons/react@^2.1.1 @internationalized/date@3.10.0
|
|
385
|
+
`${addCommand} react@^19.0.0 react-dom@^19.0.0 @astrojs/react@^4.4.2 @astrojs/node@^9.4.3 @nanostores/react@^1.0.0 nanostores@^1.0.1 @nanostores/persistent ulid@^3.0.1 @ark-ui/react@^5.30.0 @heroicons/react@^2.1.1 @internationalized/date@3.10.0 d3@^7.9.0 d3-sankey@^0.12.3 recharts@^3.1.2 player.js@^0.1.0 tinycolor2@1.6.0 html-to-image@^1.11.13 path-to-regexp@^8.0.0 postcss postcss-selector-parser`
|
|
386
386
|
)
|
|
387
387
|
);
|
|
388
388
|
console.log(
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ function b(t) {
|
|
|
10
10
|
}
|
|
11
11
|
function g(t, e) {
|
|
12
12
|
e.info("TractStack configuration applied"), t.enableMultiTenant && e.info("Multi-tenant mode enabled"), t.includeExamples && e.info("Example components will be included");
|
|
13
|
-
const c = process.env.PUBLIC_GO_BACKEND,
|
|
13
|
+
const c = process.env.PUBLIC_GO_BACKEND, o = process.env.PUBLIC_TENANTID;
|
|
14
14
|
if (!c)
|
|
15
15
|
e.warn("PUBLIC_GO_BACKEND not set - this will be required at runtime");
|
|
16
16
|
else
|
|
@@ -19,11 +19,11 @@ function g(t, e) {
|
|
|
19
19
|
} catch {
|
|
20
20
|
e.error(`PUBLIC_GO_BACKEND is not a valid URL: ${c}`);
|
|
21
21
|
}
|
|
22
|
-
return
|
|
22
|
+
return o ? /^[a-zA-Z0-9_-]+$/.test(o) ? e.info(`Tenant ID validated: ${o}`) : e.error(`PUBLIC_TENANTID contains invalid characters: ${o}`) : e.warn("PUBLIC_TENANTID not set - this will be required at runtime"), t;
|
|
23
23
|
}
|
|
24
24
|
async function y(t, e, c) {
|
|
25
25
|
e.info("TractStack: Injecting template files");
|
|
26
|
-
const
|
|
26
|
+
const o = [
|
|
27
27
|
// Core Configuration
|
|
28
28
|
{
|
|
29
29
|
src: t("../templates/env.example"),
|
|
@@ -815,6 +815,10 @@ async function y(t, e, c) {
|
|
|
815
815
|
src: t("../templates/src/utils/api/resourceHelpers.ts"),
|
|
816
816
|
dest: "src/utils/api/resourceHelpers.ts"
|
|
817
817
|
},
|
|
818
|
+
{
|
|
819
|
+
src: t("../templates/src/utils/api/bookingHelpers.ts"),
|
|
820
|
+
dest: "src/utils/api/bookingHelpers.ts"
|
|
821
|
+
},
|
|
818
822
|
{
|
|
819
823
|
src: t("../templates/src/utils/api/menuHelpers.ts"),
|
|
820
824
|
dest: "src/utils/api/menuHelpers.ts"
|
|
@@ -937,6 +941,38 @@ async function y(t, e, c) {
|
|
|
937
941
|
dest: "src/pages/storykeep/profile.astro"
|
|
938
942
|
},
|
|
939
943
|
// API Routes
|
|
944
|
+
{
|
|
945
|
+
src: t("../templates/src/pages/api/booking/list.ts"),
|
|
946
|
+
dest: "src/pages/api/booking/list.ts"
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
src: t("../templates/src/pages/api/booking/metrics.ts"),
|
|
950
|
+
dest: "src/pages/api/booking/metrics.ts"
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
src: t("../templates/src/pages/api/booking/cancel.ts"),
|
|
954
|
+
dest: "src/pages/api/booking/cancel.ts"
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
src: t("../templates/src/pages/api/booking/confirm.ts"),
|
|
958
|
+
dest: "src/pages/api/booking/confirm.ts"
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
src: t("../templates/src/pages/api/booking/release.ts"),
|
|
962
|
+
dest: "src/pages/api/booking/release.ts"
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
src: t("../templates/src/pages/api/booking/availability.ts"),
|
|
966
|
+
dest: "src/pages/api/booking/availability.ts"
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
src: t("../templates/src/pages/api/booking/hold.ts"),
|
|
970
|
+
dest: "src/pages/api/booking/hold.ts"
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
src: t("../templates/src/pages/api/auth/lookup-lead.ts"),
|
|
974
|
+
dest: "src/pages/api/auth/lookup-lead.ts"
|
|
975
|
+
},
|
|
940
976
|
{
|
|
941
977
|
src: t("../templates/src/pages/api/auth/profile.ts"),
|
|
942
978
|
dest: "src/pages/api/auth/profile.ts"
|
|
@@ -1118,6 +1154,12 @@ async function y(t, e, c) {
|
|
|
1118
1154
|
src: t("../templates/src/components/form/brand/SEOSection.tsx"),
|
|
1119
1155
|
dest: "src/components/form/brand/SEOSection.tsx"
|
|
1120
1156
|
},
|
|
1157
|
+
{
|
|
1158
|
+
src: t(
|
|
1159
|
+
"../templates/src/components/form/shopify/SchedulingSection.tsx"
|
|
1160
|
+
),
|
|
1161
|
+
dest: "src/components/form/shopify/SchedulingSection.tsx"
|
|
1162
|
+
},
|
|
1121
1163
|
// Advanced Configuration Components
|
|
1122
1164
|
{
|
|
1123
1165
|
src: t(
|
|
@@ -1170,6 +1212,72 @@ async function y(t, e, c) {
|
|
|
1170
1212
|
),
|
|
1171
1213
|
dest: "src/components/storykeep/Dashboard_Shopify.tsx"
|
|
1172
1214
|
},
|
|
1215
|
+
{
|
|
1216
|
+
src: t(
|
|
1217
|
+
"../templates/src/components/storykeep/shopify/ShopifyDashboard.tsx"
|
|
1218
|
+
),
|
|
1219
|
+
dest: "src/components/storykeep/shopify/ShopifyDashboard.tsx"
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
src: t(
|
|
1223
|
+
"../templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx"
|
|
1224
|
+
),
|
|
1225
|
+
dest: "src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx"
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
src: t(
|
|
1229
|
+
"../templates/src/components/storykeep/shopify/ShopifyDashboard_Schedule.tsx"
|
|
1230
|
+
),
|
|
1231
|
+
dest: "src/components/storykeep/shopify/ShopifyDashboard_Schedule.tsx"
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
src: t(
|
|
1235
|
+
"../templates/src/components/storykeep/shopify/ShopifyDashboard_Products.tsx"
|
|
1236
|
+
),
|
|
1237
|
+
dest: "src/components/storykeep/shopify/ShopifyDashboard_Products.tsx"
|
|
1238
|
+
},
|
|
1239
|
+
{
|
|
1240
|
+
src: t(
|
|
1241
|
+
"../templates/src/components/storykeep/shopify/ShopifyDashboard_Services.tsx"
|
|
1242
|
+
),
|
|
1243
|
+
dest: "src/components/storykeep/shopify/ShopifyDashboard_Services.tsx"
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
src: t(
|
|
1247
|
+
"../templates/src/components/storykeep/shopify/ShopifyDashboard_Emails.tsx"
|
|
1248
|
+
),
|
|
1249
|
+
dest: "src/components/storykeep/shopify/ShopifyDashboard_Emails.tsx"
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
src: t(
|
|
1253
|
+
"../templates/src/components/storykeep/email-builder/EmailBuilder.tsx"
|
|
1254
|
+
),
|
|
1255
|
+
dest: "src/components/storykeep/email-builder/EmailBuilder.tsx"
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
src: t(
|
|
1259
|
+
"../templates/src/components/storykeep/email-builder/Blocks.tsx"
|
|
1260
|
+
),
|
|
1261
|
+
dest: "src/components/storykeep/email-builder/Blocks.tsx"
|
|
1262
|
+
},
|
|
1263
|
+
{
|
|
1264
|
+
src: t(
|
|
1265
|
+
"../templates/src/components/storykeep/email-builder/PropertyPanel.tsx"
|
|
1266
|
+
),
|
|
1267
|
+
dest: "src/components/storykeep/email-builder/PropertyPanel.tsx"
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
src: t(
|
|
1271
|
+
"../templates/src/components/storykeep/email-builder/PreviewModal.tsx"
|
|
1272
|
+
),
|
|
1273
|
+
dest: "src/components/storykeep/email-builder/PreviewModal.tsx"
|
|
1274
|
+
},
|
|
1275
|
+
{
|
|
1276
|
+
src: t(
|
|
1277
|
+
"../templates/src/components/storykeep/shopify/ShopifyDashboard_Search.tsx"
|
|
1278
|
+
),
|
|
1279
|
+
dest: "src/components/storykeep/shopify/ShopifyDashboard_Search.tsx"
|
|
1280
|
+
},
|
|
1173
1281
|
{
|
|
1174
1282
|
src: t(
|
|
1175
1283
|
"../templates/src/components/storykeep/Dashboard_Analytics.tsx"
|
|
@@ -2152,6 +2260,10 @@ async function y(t, e, c) {
|
|
|
2152
2260
|
src: t("../templates/src/utils/api/setupHelpers.ts"),
|
|
2153
2261
|
dest: "src/utils/api/setupHelpers.ts"
|
|
2154
2262
|
},
|
|
2263
|
+
{
|
|
2264
|
+
src: t("../templates/src/utils/api/emailHelpers.ts"),
|
|
2265
|
+
dest: "src/utils/api/emailHelpers.ts"
|
|
2266
|
+
},
|
|
2155
2267
|
{
|
|
2156
2268
|
src: t(
|
|
2157
2269
|
"../templates/src/components/storykeep/widgets/HydrateWizard.tsx"
|
|
@@ -2231,13 +2343,13 @@ async function y(t, e, c) {
|
|
|
2231
2343
|
protected: !0
|
|
2232
2344
|
},
|
|
2233
2345
|
{
|
|
2234
|
-
src: t("../templates/custom/shopify/
|
|
2235
|
-
dest: "src/custom/shopify/
|
|
2346
|
+
src: t("../templates/custom/shopify/ShopifyCheckout.tsx"),
|
|
2347
|
+
dest: "src/custom/shopify/ShopifyCheckout.tsx",
|
|
2236
2348
|
protected: !0
|
|
2237
2349
|
},
|
|
2238
2350
|
{
|
|
2239
|
-
src: t("../templates/custom/shopify/
|
|
2240
|
-
dest: "src/custom/shopify/
|
|
2351
|
+
src: t("../templates/custom/shopify/NativeBookingCalendar.tsx"),
|
|
2352
|
+
dest: "src/custom/shopify/NativeBookingCalendar.tsx",
|
|
2241
2353
|
protected: !0
|
|
2242
2354
|
},
|
|
2243
2355
|
// Example Components (Conditional)
|
|
@@ -2285,41 +2397,40 @@ async function y(t, e, c) {
|
|
|
2285
2397
|
}
|
|
2286
2398
|
] : []
|
|
2287
2399
|
];
|
|
2288
|
-
for (const s of
|
|
2400
|
+
for (const s of o)
|
|
2289
2401
|
try {
|
|
2290
2402
|
const p = i(s.dest);
|
|
2291
2403
|
n(p) || x(p, { recursive: !0 });
|
|
2292
|
-
const
|
|
2293
|
-
if (!n(s.dest) ||
|
|
2404
|
+
const r = !s.protected && (s.dest === "tailwind.config.cjs" || s.dest.startsWith("src/components/codehooks/") || s.dest.startsWith("src/components/widgets/") || s.dest.startsWith("src/") || s.dest.startsWith("public/client/") || s.dest === ".gitignore");
|
|
2405
|
+
if (!n(s.dest) || r)
|
|
2294
2406
|
if (n(s.src))
|
|
2295
2407
|
k(s.src, s.dest), e.info(`Updated ${s.dest}`);
|
|
2296
2408
|
else {
|
|
2297
|
-
const m =
|
|
2409
|
+
const m = f(s.dest);
|
|
2298
2410
|
u(s.dest, m), e.info(`Created placeholder ${s.dest}`);
|
|
2299
2411
|
}
|
|
2300
2412
|
else s.protected ? e.info(`Protected: ${s.dest} (skipped overwrite)`) : e.info(`Skipped existing ${s.dest}`);
|
|
2301
2413
|
} catch (p) {
|
|
2302
|
-
const
|
|
2303
|
-
e.error(`Failed to create ${s.dest}: ${
|
|
2414
|
+
const r = p instanceof Error ? p.message : String(p);
|
|
2415
|
+
e.error(`Failed to create ${s.dest}: ${r}`);
|
|
2304
2416
|
}
|
|
2305
2417
|
}
|
|
2306
|
-
function
|
|
2418
|
+
function f(t) {
|
|
2307
2419
|
return t.endsWith(".astro") ? `---
|
|
2308
2420
|
// TractStack placeholder component
|
|
2309
2421
|
---
|
|
2310
2422
|
<div>TractStack placeholder: ${t}</div>` : t.endsWith(".tsx") ? `// TractStack placeholder component
|
|
2311
|
-
import React from 'react';
|
|
2312
2423
|
export default function Placeholder() {
|
|
2313
2424
|
return <div>TractStack placeholder: ${t}</div>;
|
|
2314
2425
|
}` : t.endsWith(".ts") ? `// TractStack placeholder utility
|
|
2315
2426
|
export const placeholder = "${t}";` : `# TractStack placeholder: ${t}`;
|
|
2316
2427
|
}
|
|
2317
|
-
function
|
|
2428
|
+
function P(t = {}) {
|
|
2318
2429
|
const { resolve: e } = b(import.meta.url);
|
|
2319
2430
|
return {
|
|
2320
2431
|
name: "astro-tractstack",
|
|
2321
2432
|
hooks: {
|
|
2322
|
-
"astro:config:setup": async ({ config: c, updateConfig:
|
|
2433
|
+
"astro:config:setup": async ({ config: c, updateConfig: o, logger: s }) => {
|
|
2323
2434
|
g(t, s);
|
|
2324
2435
|
const p = t.enableMultiTenant || !1;
|
|
2325
2436
|
if (s.info(
|
|
@@ -2339,7 +2450,7 @@ function h(t = {}) {
|
|
|
2339
2450
|
), new Error(
|
|
2340
2451
|
"TractStack requires an SSR adapter. Please add @astrojs/node adapter to your astro.config.mjs"
|
|
2341
2452
|
);
|
|
2342
|
-
|
|
2453
|
+
o({
|
|
2343
2454
|
vite: {
|
|
2344
2455
|
define: {
|
|
2345
2456
|
__TRACTSTACK_VERSION__: JSON.stringify("2.0.0-alpha.1"),
|
|
@@ -2396,5 +2507,5 @@ function h(t = {}) {
|
|
|
2396
2507
|
};
|
|
2397
2508
|
}
|
|
2398
2509
|
export {
|
|
2399
|
-
|
|
2510
|
+
P as default
|
|
2400
2511
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-tractstack",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"description": "Astro integration for TractStack - the free web press by At Risk Media",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@heroicons/react": "^2.1.1",
|
|
62
62
|
"@internationalized/date": "^3.10.1",
|
|
63
63
|
"@mhsdesign/jit-browser-tailwindcss": "^0.4.2",
|
|
64
|
-
"@nanostores/persistent": "^1.
|
|
64
|
+
"@nanostores/persistent": "^1.3.3",
|
|
65
65
|
"@nanostores/react": "^1.0.0",
|
|
66
66
|
"@types/d3": "^7.4.3",
|
|
67
67
|
"@types/d3-sankey": "^0.12.5",
|
|
@@ -50,9 +50,17 @@ export const components = {
|
|
|
50
50
|
) : target === 'epinet' ? (
|
|
51
51
|
<EpinetWrapper fullContentMap={fullContentMap} client:only="react" />
|
|
52
52
|
) : target === 'shopify-product-grid' ? (
|
|
53
|
-
<ShopifyProductGrid
|
|
53
|
+
<ShopifyProductGrid
|
|
54
|
+
options={options}
|
|
55
|
+
resources={resourcesPayload}
|
|
56
|
+
client:only="react"
|
|
57
|
+
/>
|
|
54
58
|
) : target === 'shopify-service-list' ? (
|
|
55
|
-
<ShopifyServiceList
|
|
59
|
+
<ShopifyServiceList
|
|
60
|
+
options={options}
|
|
61
|
+
resources={resourcesPayload}
|
|
62
|
+
client:only="react"
|
|
63
|
+
/>
|
|
56
64
|
) : (
|
|
57
65
|
/* : target === "custom-hero" ? (
|
|
58
66
|
<CustomHero />
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
|
+
import { ulid } from 'ulid';
|
|
2
3
|
import { useStore } from '@nanostores/react';
|
|
3
4
|
import {
|
|
4
5
|
addQueue,
|
|
@@ -6,7 +7,7 @@ import {
|
|
|
6
7
|
cartState,
|
|
7
8
|
CART_STATES,
|
|
8
9
|
isShopifyHandoff,
|
|
9
|
-
|
|
10
|
+
transactionTraceId,
|
|
10
11
|
type CartItemState,
|
|
11
12
|
} from '@/stores/shopify';
|
|
12
13
|
import { getShopifyImage } from '@/utils/helpers';
|
|
@@ -18,12 +19,19 @@ interface CartProps {
|
|
|
18
19
|
|
|
19
20
|
const getCleanVariantTitle = (variant: any) => {
|
|
20
21
|
if (variant?.selectedOptions) {
|
|
21
|
-
|
|
22
|
-
.filter(
|
|
22
|
+
const filtered = variant.selectedOptions
|
|
23
|
+
.filter(
|
|
24
|
+
(o: any) =>
|
|
25
|
+
o.name !== 'Mode' && o.name !== 'Title' && o.value !== 'Default Title'
|
|
26
|
+
)
|
|
23
27
|
.map((o: any) => o.value)
|
|
24
28
|
.join(' / ');
|
|
29
|
+
|
|
30
|
+
return filtered === 'Default Title' ? '' : filtered;
|
|
25
31
|
}
|
|
26
|
-
|
|
32
|
+
|
|
33
|
+
const title = variant?.title || '';
|
|
34
|
+
return title === 'Default Title' ? '' : title;
|
|
27
35
|
};
|
|
28
36
|
|
|
29
37
|
export default function Cart({ resources = [] }: CartProps) {
|
|
@@ -39,8 +47,6 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
39
47
|
.filter((id) => !!id) as string[]
|
|
40
48
|
);
|
|
41
49
|
|
|
42
|
-
const hasServiceBoundProduct = boundServiceIds.size > 0;
|
|
43
|
-
|
|
44
50
|
const displayableItems = cartValues.filter(
|
|
45
51
|
(item) => !boundServiceIds.has(item.resourceId)
|
|
46
52
|
);
|
|
@@ -62,61 +68,57 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
62
68
|
});
|
|
63
69
|
|
|
64
70
|
const hasPhysicalProductWithPickup = cartValues.some(
|
|
65
|
-
(item) =>
|
|
71
|
+
(item) =>
|
|
72
|
+
item.variantIdPickup && item.variantIdPickup !== item.variantIdShipped
|
|
66
73
|
);
|
|
67
74
|
|
|
68
75
|
const canPickup = hasService && hasPhysicalProductWithPickup;
|
|
69
76
|
|
|
70
77
|
useEffect(() => {
|
|
71
|
-
if (
|
|
72
|
-
setPickupEnabled(true);
|
|
73
|
-
} else if (canPickup) {
|
|
78
|
+
if (canPickup) {
|
|
74
79
|
setPickupEnabled(true);
|
|
75
80
|
} else {
|
|
76
81
|
setPickupEnabled(false);
|
|
77
82
|
}
|
|
78
|
-
}, [canPickup
|
|
79
|
-
|
|
80
|
-
const isPickupMode =
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
queueUpdates.push({
|
|
89
|
-
resourceId: item.resourceId,
|
|
90
|
-
action,
|
|
91
|
-
variantId: item.variantId,
|
|
92
|
-
variantIdShipped: item.variantIdShipped,
|
|
93
|
-
variantIdPickup: item.variantIdPickup,
|
|
94
|
-
boundResourceId: item.boundResourceId,
|
|
95
|
-
suppressModal: action === 'add' ? true : undefined,
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
if (item.boundResourceId) {
|
|
99
|
-
queueUpdates.push({
|
|
100
|
-
resourceId: item.boundResourceId,
|
|
83
|
+
}, [canPickup]);
|
|
84
|
+
|
|
85
|
+
const isPickupMode = canPickup && pickupEnabled;
|
|
86
|
+
|
|
87
|
+
const dispatchAction = (item: CartItemState, action: 'add' | 'remove') => {
|
|
88
|
+
addQueue.set([
|
|
89
|
+
...addQueue.get(),
|
|
90
|
+
{
|
|
91
|
+
resourceId: item.resourceId,
|
|
101
92
|
action,
|
|
93
|
+
variantId: item.variantId,
|
|
94
|
+
variantIdShipped: item.variantIdShipped,
|
|
95
|
+
variantIdPickup: item.variantIdPickup,
|
|
96
|
+
boundResourceId: item.boundResourceId,
|
|
102
97
|
suppressModal: action === 'add' ? true : undefined,
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
addQueue.set([...addQueue.get(), ...queueUpdates]);
|
|
98
|
+
},
|
|
99
|
+
]);
|
|
107
100
|
};
|
|
108
101
|
|
|
102
|
+
if (isHandoff) {
|
|
103
|
+
return (
|
|
104
|
+
<div
|
|
105
|
+
className="fixed inset-0 flex flex-col items-center justify-center bg-black bg-opacity-75 backdrop-blur-md"
|
|
106
|
+
style={{ zIndex: 9005 }}
|
|
107
|
+
>
|
|
108
|
+
<div className="h-12 w-12 animate-spin rounded-full border-4 border-gray-200 border-t-white"></div>
|
|
109
|
+
<h3 className="mt-4 text-lg font-bold text-white">
|
|
110
|
+
Finalizing Handoff...
|
|
111
|
+
</h3>
|
|
112
|
+
<p className="mt-2 text-sm text-gray-300">
|
|
113
|
+
Redirecting to Shopify secured payment
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
109
119
|
if (cartValues.length === 0) {
|
|
110
120
|
return (
|
|
111
121
|
<div className="relative">
|
|
112
|
-
{isHandoff && (
|
|
113
|
-
<div className="absolute inset-0 z-103 flex flex-col items-center justify-center rounded-lg bg-black bg-opacity-75 backdrop-blur-md">
|
|
114
|
-
<div className="h-12 w-12 animate-spin rounded-full border-4 border-gray-200 border-t-black"></div>
|
|
115
|
-
<h3 className="mt-4 text-lg font-bold text-gray-900">
|
|
116
|
-
Finalizing Handoff...
|
|
117
|
-
</h3>
|
|
118
|
-
</div>
|
|
119
|
-
)}
|
|
120
122
|
<div className="rounded-lg border bg-gray-50 p-8 text-center">
|
|
121
123
|
<h2 className="text-xl font-bold">Your cart is empty</h2>
|
|
122
124
|
<p className="mt-2 text-gray-600">Add some items to get started.</p>
|
|
@@ -161,9 +163,10 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
161
163
|
? firstItem.variantIdPickup
|
|
162
164
|
: firstItem.variantIdShipped;
|
|
163
165
|
const displayIdFirst =
|
|
166
|
+
firstItem.variantId ||
|
|
164
167
|
activeVariantIdFirst ||
|
|
165
|
-
firstItem.variantIdPickup
|
|
166
|
-
|
|
168
|
+
firstItem.variantIdPickup;
|
|
169
|
+
|
|
167
170
|
const { src, srcSet } = getShopifyImage(
|
|
168
171
|
resource,
|
|
169
172
|
'600',
|
|
@@ -202,16 +205,27 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
202
205
|
{resource.title}
|
|
203
206
|
</h3>
|
|
204
207
|
{isService && (
|
|
205
|
-
<span className="inline-flex items-center rounded-
|
|
208
|
+
<span className="inline-flex items-center rounded-sm bg-blue-50 px-2 py-0.5 text-xs font-bold text-blue-700">
|
|
206
209
|
{serviceDuration} mins
|
|
207
210
|
</span>
|
|
208
211
|
)}
|
|
209
212
|
</div>
|
|
213
|
+
|
|
210
214
|
{boundServiceResource && (
|
|
211
|
-
<
|
|
212
|
-
<span className="
|
|
213
|
-
|
|
214
|
-
|
|
215
|
+
<div className="mt-2 flex items-center gap-2 rounded-md bg-blue-50 px-3 py-2">
|
|
216
|
+
<span className="inline-block h-2 w-2 rounded-full bg-blue-500"></span>
|
|
217
|
+
<div>
|
|
218
|
+
<p className="text-sm font-bold text-blue-900">
|
|
219
|
+
Includes Booking: {boundServiceResource.title}
|
|
220
|
+
</p>
|
|
221
|
+
<p className="text-xs text-blue-700">
|
|
222
|
+
Duration:{' '}
|
|
223
|
+
{boundServiceResource.optionsPayload
|
|
224
|
+
?.bookingLengthMinutes || 0}{' '}
|
|
225
|
+
mins
|
|
226
|
+
</p>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
215
229
|
)}
|
|
216
230
|
<p className="mt-1 text-sm text-gray-500">
|
|
217
231
|
{resource.oneliner}
|
|
@@ -220,15 +234,15 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
220
234
|
</div>
|
|
221
235
|
|
|
222
236
|
<div className="mt-4 space-y-4 border-t border-gray-100 pt-4">
|
|
223
|
-
{items.map((item) => {
|
|
237
|
+
{items.map((item, idx) => {
|
|
224
238
|
const activeVariantId = isPickupMode
|
|
225
239
|
? item.variantIdPickup
|
|
226
240
|
: item.variantIdShipped;
|
|
227
241
|
|
|
228
242
|
const displayId =
|
|
243
|
+
item.variantId ||
|
|
229
244
|
activeVariantId ||
|
|
230
|
-
item.variantIdPickup
|
|
231
|
-
item.variantId;
|
|
245
|
+
item.variantIdPickup;
|
|
232
246
|
|
|
233
247
|
let price = '0.00';
|
|
234
248
|
let currency = 'USD';
|
|
@@ -242,30 +256,31 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
242
256
|
price = variant.price?.amount || '0.00';
|
|
243
257
|
currency = variant.price?.currencyCode || 'USD';
|
|
244
258
|
variantTitle = getCleanVariantTitle(variant);
|
|
245
|
-
} else if (variants.length > 0 && !variantTitle) {
|
|
246
|
-
const v = variants[0];
|
|
247
|
-
price = v.price?.amount || '0.00';
|
|
248
|
-
currency = v.price?.currencyCode || 'USD';
|
|
249
|
-
variantTitle = getCleanVariantTitle(v);
|
|
250
259
|
}
|
|
251
260
|
|
|
252
261
|
return (
|
|
253
262
|
<div
|
|
254
|
-
key={`${item.resourceId}_${displayId}`}
|
|
263
|
+
key={`${item.resourceId}_${displayId}_${idx}`}
|
|
255
264
|
className="flex items-center justify-between"
|
|
256
265
|
>
|
|
257
266
|
<div className="flex items-center gap-2">
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
variantTitle
|
|
261
|
-
|
|
262
|
-
)}
|
|
263
|
-
</div>
|
|
264
|
-
{isPickupMode && !isService && (
|
|
265
|
-
<span className="inline-flex items-center rounded bg-gray-100 px-2 py-0.5 text-xs font-bold text-gray-800">
|
|
266
|
-
Store Pickup
|
|
267
|
-
</span>
|
|
267
|
+
{variantTitle && (
|
|
268
|
+
<div className="text-sm font-bold text-gray-700">
|
|
269
|
+
<span>{variantTitle}</span>
|
|
270
|
+
</div>
|
|
268
271
|
)}
|
|
272
|
+
{isPickupMode &&
|
|
273
|
+
!isService &&
|
|
274
|
+
(item.variantIdPickup &&
|
|
275
|
+
item.variantIdPickup !== item.variantIdShipped ? (
|
|
276
|
+
<span className="inline-flex items-center rounded bg-gray-100 px-2 py-0.5 text-xs font-bold text-gray-800">
|
|
277
|
+
Store Pickup
|
|
278
|
+
</span>
|
|
279
|
+
) : (
|
|
280
|
+
<span className="inline-flex items-center rounded bg-red-50 px-2 py-0.5 text-xs font-bold text-red-700">
|
|
281
|
+
Not available for pickup
|
|
282
|
+
</span>
|
|
283
|
+
))}
|
|
269
284
|
</div>
|
|
270
285
|
|
|
271
286
|
<div className="flex items-center">
|
|
@@ -296,9 +311,7 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
296
311
|
) : (
|
|
297
312
|
<div className="flex items-center rounded-md border border-gray-300">
|
|
298
313
|
<button
|
|
299
|
-
onClick={() =>
|
|
300
|
-
dispatchDualAction(item, 'remove')
|
|
301
|
-
}
|
|
314
|
+
onClick={() => dispatchAction(item, 'remove')}
|
|
302
315
|
className="px-3 py-1 text-gray-600 hover:bg-gray-100"
|
|
303
316
|
>
|
|
304
317
|
-
|
|
@@ -307,9 +320,7 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
307
320
|
{item.quantity}
|
|
308
321
|
</span>
|
|
309
322
|
<button
|
|
310
|
-
onClick={() =>
|
|
311
|
-
dispatchDualAction(item, 'add')
|
|
312
|
-
}
|
|
323
|
+
onClick={() => dispatchAction(item, 'add')}
|
|
313
324
|
className="px-3 py-1 text-gray-600 hover:bg-gray-100"
|
|
314
325
|
>
|
|
315
326
|
+
|
|
@@ -333,6 +344,33 @@ export default function Cart({ resources = [] }: CartProps) {
|
|
|
333
344
|
<button
|
|
334
345
|
className="rounded-lg bg-black px-6 py-3 font-bold text-white transition-colors hover:bg-gray-800"
|
|
335
346
|
onClick={() => {
|
|
347
|
+
const currentCart = cartStore.get();
|
|
348
|
+
const sanitizedCart = { ...currentCart };
|
|
349
|
+
|
|
350
|
+
Object.keys(sanitizedCart).forEach((key) => {
|
|
351
|
+
const item = { ...sanitizedCart[key] };
|
|
352
|
+
|
|
353
|
+
if (
|
|
354
|
+
item.variantId &&
|
|
355
|
+
!item.variantIdShipped &&
|
|
356
|
+
!item.variantIdPickup
|
|
357
|
+
) {
|
|
358
|
+
sanitizedCart[key] = item;
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (isPickupMode && item.variantIdPickup) {
|
|
363
|
+
item.variantId = item.variantIdPickup;
|
|
364
|
+
} else if (!isPickupMode && item.variantIdShipped) {
|
|
365
|
+
item.variantId = item.variantIdShipped;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
sanitizedCart[key] = item;
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
cartStore.set(sanitizedCart);
|
|
372
|
+
transactionTraceId.set(ulid());
|
|
373
|
+
|
|
336
374
|
cartState.set(CART_STATES.CHECKOUT);
|
|
337
375
|
}}
|
|
338
376
|
>
|