astro-tractstack 2.3.5 → 2.4.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/bin/create-tractstack.js +38 -59
- package/dist/index.js +68 -36
- package/package.json +46 -9
- package/templates/custom/minimal/codehooks.ts +13 -0
- package/templates/custom/shopify/CartModal.tsx +11 -1
- package/templates/custom/shopify/CartReset.tsx +38 -0
- package/templates/custom/shopify/ShopifyCartManager.tsx +2 -0
- package/templates/custom/shopify/ShopifyProductGrid.tsx +4 -4
- package/templates/custom/shopify/ShopifyServiceList.tsx +56 -43
- package/templates/custom/shopify/cart-reset.astro +19 -0
- package/templates/custom/with-examples/codehooks.ts +15 -0
- package/templates/src/components/Header.astro +1 -1
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +38 -23
- package/templates/src/components/codehooks/EpinetTableView.tsx +5 -2
- package/templates/src/components/codehooks/EpinetWrapper.tsx +10 -5
- package/templates/src/components/codehooks/FeaturedArticle.astro +3 -3
- package/templates/src/components/codehooks/ListContent.astro +3 -3
- package/templates/src/components/compositor/Node.tsx +13 -2
- package/templates/src/components/compositor/nodes/Pane.tsx +2 -14
- package/templates/src/components/edit/pane/AddPanePanel.tsx +3 -2
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +35 -14
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -1
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +2 -2
- package/templates/src/components/storykeep/Dashboard_Analytics.tsx +8 -4
- package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +5 -2
- package/templates/src/components/storykeep/state/FetchAnalytics.tsx +8 -4
- package/templates/src/components/storykeep/widgets/Wizard.tsx +4 -2
- package/templates/src/lib/codeHookHelper.ts +156 -0
- package/templates/src/lib/resources.ts +41 -0
- package/templates/src/lib/storyData.ts +1 -2
- package/templates/src/pages/[...slug]/edit.astro +3 -3
- package/templates/src/pages/[...slug].astro +76 -70
- package/templates/src/pages/codehooks/[...hookId].astro +18 -0
- package/templates/src/pages/codehooks/bunny-video.astro +9 -0
- package/templates/src/pages/codehooks/custom-hero.astro +6 -0
- package/templates/src/pages/codehooks/epinet.astro +15 -0
- package/templates/src/pages/codehooks/featured-article.astro +13 -0
- package/templates/src/pages/codehooks/get-crafting.astro +8 -0
- package/templates/src/pages/codehooks/list-content.astro +13 -0
- package/templates/src/pages/codehooks/search-widget.astro +13 -0
- package/templates/src/pages/codehooks/shopify-product-grid.astro +23 -0
- package/templates/src/pages/codehooks/shopify-service-list.astro +23 -0
- package/templates/src/pages/context/[...contextSlug]/edit.astro +3 -3
- package/templates/src/pages/context/[...contextSlug].astro +47 -10
- package/templates/src/pages/sandbox.astro +3 -14
- package/templates/src/stores/analytics.ts +77 -107
- package/templates/src/stores/shopify.ts +34 -4
- package/utils/inject-files.ts +70 -37
- package/templates/custom/minimal/CodeHook.astro +0 -72
- package/templates/custom/with-examples/CodeHook.astro +0 -81
- package/templates/custom/with-examples/ProductCard.astro +0 -29
- package/templates/custom/with-examples/ProductCardWrapper.astro +0 -43
- package/templates/custom/with-examples/ProductGrid.astro +0 -64
- package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -157
- package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -279
package/bin/create-tractstack.js
CHANGED
|
@@ -7,14 +7,33 @@ import {
|
|
|
7
7
|
writeFileSync,
|
|
8
8
|
existsSync,
|
|
9
9
|
} from 'fs';
|
|
10
|
-
import { resolve } from 'path';
|
|
10
|
+
import { dirname, resolve } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
11
12
|
import { homedir } from 'os';
|
|
12
13
|
import { execSync } from 'child_process';
|
|
13
14
|
import prompts from 'prompts';
|
|
14
15
|
import kleur from 'kleur';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
|
|
19
|
+
function loadInstallManifest() {
|
|
20
|
+
const pkg = JSON.parse(
|
|
21
|
+
readFileSync(resolve(__dirname, '../package.json'), 'utf-8')
|
|
22
|
+
);
|
|
23
|
+
const manifest = pkg.tractstackInstall;
|
|
24
|
+
if (!manifest?.dependencies) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
'astro-tractstack package.json is missing tractstackInstall.dependencies'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return manifest;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function formatPackageSpecs(deps) {
|
|
33
|
+
return Object.entries(deps)
|
|
34
|
+
.map(([name, version]) => (version ? `${name}@${version}` : name))
|
|
35
|
+
.join(' ');
|
|
36
|
+
}
|
|
18
37
|
|
|
19
38
|
// Detect package manager
|
|
20
39
|
function detectPackageManager() {
|
|
@@ -332,67 +351,27 @@ PUBLIC_ENABLE_BUNNY="${finalResponses.enableBunny ? 'true' : 'false'}"
|
|
|
332
351
|
packageManager === 'pnpm' ? 'pnpm add' : `${packageManager} add`;
|
|
333
352
|
|
|
334
353
|
console.log(kleur.cyan('\nInstalling dependencies...'));
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
);
|
|
341
|
-
console.log(kleur.green('✅ React and Node adapter installed'));
|
|
342
|
-
|
|
343
|
-
// Install core dependencies
|
|
344
|
-
execSync(
|
|
345
|
-
`${addCommand} @nanostores/react@^1.0.0 nanostores@^1.0.1 @nanostores/persistent ulid@^3.0.1`,
|
|
346
|
-
{
|
|
347
|
-
stdio: 'inherit',
|
|
348
|
-
}
|
|
349
|
-
);
|
|
350
|
-
console.log(kleur.green('✅ State management installed'));
|
|
351
|
-
|
|
352
|
-
// Install UI components
|
|
353
|
-
execSync(
|
|
354
|
-
`${addCommand} @ark-ui/react@^5.30.0 @heroicons/react@^2.1.1 @internationalized/date@${INTL_DATE_VERSION}`,
|
|
355
|
-
{
|
|
356
|
-
stdio: 'inherit',
|
|
357
|
-
}
|
|
358
|
-
);
|
|
359
|
-
console.log(kleur.green('✅ UI components installed'));
|
|
360
|
-
|
|
361
|
-
// Install visualization dependencies
|
|
362
|
-
execSync(
|
|
363
|
-
`${addCommand} 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`,
|
|
364
|
-
{
|
|
365
|
-
stdio: 'inherit',
|
|
366
|
-
}
|
|
367
|
-
);
|
|
368
|
-
console.log(kleur.green('✅ Visualization dependencies installed'));
|
|
354
|
+
const installManifest = loadInstallManifest();
|
|
355
|
+
const dependencySpecs = formatPackageSpecs(installManifest.dependencies);
|
|
356
|
+
const devDependencySpecs = formatPackageSpecs(
|
|
357
|
+
installManifest.devDependencies || {}
|
|
358
|
+
);
|
|
369
359
|
|
|
370
|
-
|
|
371
|
-
execSync(
|
|
372
|
-
|
|
373
|
-
{ stdio: 'inherit' }
|
|
374
|
-
);
|
|
375
|
-
console.log(kleur.green('✅ Additional dependencies installed'));
|
|
360
|
+
try {
|
|
361
|
+
execSync(`${addCommand} ${dependencySpecs}`, { stdio: 'inherit' });
|
|
362
|
+
console.log(kleur.green('✅ TractStack dependencies installed'));
|
|
376
363
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
);
|
|
382
|
-
console.log(kleur.green('✅ Dev dependencies installed'));
|
|
364
|
+
if (devDependencySpecs) {
|
|
365
|
+
execSync(`${addCommand} -D ${devDependencySpecs}`, { stdio: 'inherit' });
|
|
366
|
+
console.log(kleur.green('✅ Dev dependencies installed'));
|
|
367
|
+
}
|
|
383
368
|
} catch (error) {
|
|
384
369
|
console.log(kleur.red('❌ Failed to install dependencies'));
|
|
385
370
|
console.log('Please run manually:');
|
|
386
|
-
console.log(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
);
|
|
391
|
-
console.log(
|
|
392
|
-
kleur.cyan(
|
|
393
|
-
`${addCommand} -D @types/node@^22.18.0 @types/react@^19.0.0 @types/react-dom@^19.0.0 @types/d3@^7.4.3 @types/d3-sankey@^0.12.3 prettier@^3.7.4 prettier-plugin-astro@^0.14.1 prettier-plugin-tailwindcss@^0.7.2 typescript@^5.9.3 @types/tinycolor2@^1.4.6 @mhsdesign/jit-browser-tailwindcss@^0.4.2`
|
|
394
|
-
)
|
|
395
|
-
);
|
|
371
|
+
console.log(kleur.cyan(`${addCommand} ${dependencySpecs}`));
|
|
372
|
+
if (devDependencySpecs) {
|
|
373
|
+
console.log(kleur.cyan(`${addCommand} -D ${devDependencySpecs}`));
|
|
374
|
+
}
|
|
396
375
|
process.exit(1);
|
|
397
376
|
}
|
|
398
377
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fileURLToPath as d } from "node:url";
|
|
2
2
|
import { dirname as i, resolve as l } from "node:path";
|
|
3
|
-
import { existsSync as n, mkdirSync as
|
|
3
|
+
import { existsSync as n, mkdirSync as k, copyFileSync as x, writeFileSync as u } from "node:fs";
|
|
4
4
|
import { resolve as a } from "path";
|
|
5
5
|
function g(t) {
|
|
6
6
|
const e = i(d(t));
|
|
@@ -891,6 +891,14 @@ async function y(t, e, c) {
|
|
|
891
891
|
src: t("../templates/custom/shopify/cart.astro"),
|
|
892
892
|
dest: "src/pages/cart.astro"
|
|
893
893
|
},
|
|
894
|
+
{
|
|
895
|
+
src: t("../templates/custom/shopify/cart-reset.astro"),
|
|
896
|
+
dest: "src/pages/cart/reset.astro"
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
src: t("../templates/custom/shopify/CartReset.tsx"),
|
|
900
|
+
dest: "src/custom/shopify/CartReset.tsx"
|
|
901
|
+
},
|
|
894
902
|
{
|
|
895
903
|
src: t("../templates/src/pages/privacy.astro"),
|
|
896
904
|
dest: "src/pages/privacy.astro"
|
|
@@ -1497,18 +1505,6 @@ async function y(t, e, c) {
|
|
|
1497
1505
|
),
|
|
1498
1506
|
dest: "src/components/codehooks/ListContentSetup.tsx"
|
|
1499
1507
|
},
|
|
1500
|
-
{
|
|
1501
|
-
src: t(
|
|
1502
|
-
"../templates/src/components/codehooks/ProductCardSetup.tsx"
|
|
1503
|
-
),
|
|
1504
|
-
dest: "src/components/codehooks/ProductCardSetup.tsx"
|
|
1505
|
-
},
|
|
1506
|
-
{
|
|
1507
|
-
src: t(
|
|
1508
|
-
"../templates/src/components/codehooks/ProductGridSetup.tsx"
|
|
1509
|
-
),
|
|
1510
|
-
dest: "src/components/codehooks/ProductGridSetup.tsx"
|
|
1511
|
-
},
|
|
1512
1508
|
{
|
|
1513
1509
|
src: t(
|
|
1514
1510
|
"../templates/src/components/codehooks/BunnyVideoWrapper.astro"
|
|
@@ -1519,6 +1515,31 @@ async function y(t, e, c) {
|
|
|
1519
1515
|
src: t("../templates/src/components/codehooks/BunnyVideoSetup.tsx"),
|
|
1520
1516
|
dest: "src/components/codehooks/BunnyVideoSetup.tsx"
|
|
1521
1517
|
},
|
|
1518
|
+
// CodeHook Blades (partial routes; core hooks overwritable)
|
|
1519
|
+
{
|
|
1520
|
+
src: t("../templates/src/pages/codehooks/featured-article.astro"),
|
|
1521
|
+
dest: "src/pages/codehooks/featured-article.astro"
|
|
1522
|
+
},
|
|
1523
|
+
{
|
|
1524
|
+
src: t("../templates/src/pages/codehooks/list-content.astro"),
|
|
1525
|
+
dest: "src/pages/codehooks/list-content.astro"
|
|
1526
|
+
},
|
|
1527
|
+
{
|
|
1528
|
+
src: t("../templates/src/pages/codehooks/search-widget.astro"),
|
|
1529
|
+
dest: "src/pages/codehooks/search-widget.astro"
|
|
1530
|
+
},
|
|
1531
|
+
{
|
|
1532
|
+
src: t("../templates/src/pages/codehooks/bunny-video.astro"),
|
|
1533
|
+
dest: "src/pages/codehooks/bunny-video.astro"
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
src: t("../templates/src/pages/codehooks/epinet.astro"),
|
|
1537
|
+
dest: "src/pages/codehooks/epinet.astro"
|
|
1538
|
+
},
|
|
1539
|
+
{
|
|
1540
|
+
src: t("../templates/src/pages/codehooks/[...hookId].astro"),
|
|
1541
|
+
dest: "src/pages/codehooks/[...hookId].astro"
|
|
1542
|
+
},
|
|
1522
1543
|
// Widget Components
|
|
1523
1544
|
{
|
|
1524
1545
|
src: t("../templates/src/components/widgets/Impression.tsx"),
|
|
@@ -1545,6 +1566,10 @@ async function y(t, e, c) {
|
|
|
1545
1566
|
src: t("../templates/src/lib/resources.ts"),
|
|
1546
1567
|
dest: "src/lib/resources.ts"
|
|
1547
1568
|
},
|
|
1569
|
+
{
|
|
1570
|
+
src: t("../templates/src/lib/codeHookHelper.ts"),
|
|
1571
|
+
dest: "src/lib/codeHookHelper.ts"
|
|
1572
|
+
},
|
|
1548
1573
|
// Client Scripts
|
|
1549
1574
|
{
|
|
1550
1575
|
src: t("../templates/src/client/htmx.min.js"),
|
|
@@ -2319,13 +2344,6 @@ async function y(t, e, c) {
|
|
|
2319
2344
|
dest: "src/pages/storykeep/init.astro"
|
|
2320
2345
|
},
|
|
2321
2346
|
// Custom Components (Conditional)
|
|
2322
|
-
{
|
|
2323
|
-
src: t(
|
|
2324
|
-
c?.includeExamples ? "../templates/custom/with-examples/CodeHook.astro" : "../templates/custom/minimal/CodeHook.astro"
|
|
2325
|
-
),
|
|
2326
|
-
dest: "src/custom/CodeHook.astro",
|
|
2327
|
-
protected: !0
|
|
2328
|
-
},
|
|
2329
2347
|
{
|
|
2330
2348
|
src: t(
|
|
2331
2349
|
c?.includeExamples ? "../templates/custom/with-examples/CustomRoutes.astro" : "../templates/custom/minimal/CustomRoutes.astro"
|
|
@@ -2334,9 +2352,7 @@ async function y(t, e, c) {
|
|
|
2334
2352
|
protected: !0
|
|
2335
2353
|
},
|
|
2336
2354
|
{
|
|
2337
|
-
src: t(
|
|
2338
|
-
c?.includeExamples ? "../templates/custom/with-examples/HeaderWidget.astro" : "../templates/custom/minimal/HeaderWidget.astro"
|
|
2339
|
-
),
|
|
2355
|
+
src: t("../templates/custom/minimal/HeaderWidget.astro"),
|
|
2340
2356
|
dest: "src/custom/HeaderWidget.astro",
|
|
2341
2357
|
protected: !0
|
|
2342
2358
|
},
|
|
@@ -2405,6 +2421,29 @@ async function y(t, e, c) {
|
|
|
2405
2421
|
dest: "src/custom/shopify/NativeBookingCalendar.tsx",
|
|
2406
2422
|
protected: !0
|
|
2407
2423
|
},
|
|
2424
|
+
// CodeHook manifest (frontend capability list; hand-maintained, protected)
|
|
2425
|
+
{
|
|
2426
|
+
src: t(
|
|
2427
|
+
c?.includeExamples ? "../templates/custom/with-examples/codehooks.ts" : "../templates/custom/minimal/codehooks.ts"
|
|
2428
|
+
),
|
|
2429
|
+
dest: "src/custom/codehooks.ts",
|
|
2430
|
+
protected: !0
|
|
2431
|
+
},
|
|
2432
|
+
// CodeHook Blades (userland hooks; protected once installed)
|
|
2433
|
+
{
|
|
2434
|
+
src: t(
|
|
2435
|
+
"../templates/src/pages/codehooks/shopify-product-grid.astro"
|
|
2436
|
+
),
|
|
2437
|
+
dest: "src/pages/codehooks/shopify-product-grid.astro",
|
|
2438
|
+
protected: !0
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
src: t(
|
|
2442
|
+
"../templates/src/pages/codehooks/shopify-service-list.astro"
|
|
2443
|
+
),
|
|
2444
|
+
dest: "src/pages/codehooks/shopify-service-list.astro",
|
|
2445
|
+
protected: !0
|
|
2446
|
+
},
|
|
2408
2447
|
// Example Components (Conditional)
|
|
2409
2448
|
...c?.includeExamples ? [
|
|
2410
2449
|
{
|
|
@@ -2420,20 +2459,13 @@ async function y(t, e, c) {
|
|
|
2420
2459
|
protected: !0
|
|
2421
2460
|
},
|
|
2422
2461
|
{
|
|
2423
|
-
src: t("../templates/custom
|
|
2424
|
-
dest: "src/custom
|
|
2425
|
-
protected: !0
|
|
2426
|
-
},
|
|
2427
|
-
{
|
|
2428
|
-
src: t(
|
|
2429
|
-
"../templates/custom/with-examples/ProductCardWrapper.astro"
|
|
2430
|
-
),
|
|
2431
|
-
dest: "src/custom/ProductCardWrapper.astro",
|
|
2462
|
+
src: t("../templates/src/pages/codehooks/custom-hero.astro"),
|
|
2463
|
+
dest: "src/pages/codehooks/custom-hero.astro",
|
|
2432
2464
|
protected: !0
|
|
2433
2465
|
},
|
|
2434
2466
|
{
|
|
2435
|
-
src: t("../templates/
|
|
2436
|
-
dest: "src/
|
|
2467
|
+
src: t("../templates/src/pages/codehooks/get-crafting.astro"),
|
|
2468
|
+
dest: "src/pages/codehooks/get-crafting.astro",
|
|
2437
2469
|
protected: !0
|
|
2438
2470
|
},
|
|
2439
2471
|
{
|
|
@@ -2453,11 +2485,11 @@ async function y(t, e, c) {
|
|
|
2453
2485
|
for (const s of o)
|
|
2454
2486
|
try {
|
|
2455
2487
|
const p = i(s.dest);
|
|
2456
|
-
n(p) ||
|
|
2488
|
+
n(p) || k(p, { recursive: !0 });
|
|
2457
2489
|
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");
|
|
2458
2490
|
if (!n(s.dest) || r)
|
|
2459
2491
|
if (n(s.src))
|
|
2460
|
-
|
|
2492
|
+
x(s.src, s.dest), e.info(`Updated ${s.dest}`);
|
|
2461
2493
|
else {
|
|
2462
2494
|
const m = f(s.dest);
|
|
2463
2495
|
u(s.dest, m), e.info(`Created placeholder ${s.dest}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-tractstack",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
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",
|
|
@@ -50,19 +50,57 @@
|
|
|
50
50
|
"react": "^19.0.0",
|
|
51
51
|
"react-dom": "^19.0.0"
|
|
52
52
|
},
|
|
53
|
+
"tractstackInstall": {
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"react": "^19.0.0",
|
|
56
|
+
"react-dom": "^19.0.0",
|
|
57
|
+
"astro": "^5.16.6",
|
|
58
|
+
"@astrojs/react": "^4.4.2",
|
|
59
|
+
"@astrojs/node": "^9.4.3",
|
|
60
|
+
"@nanostores/react": "^1.1.0",
|
|
61
|
+
"nanostores": "^1.3.0",
|
|
62
|
+
"@nanostores/persistent": "^1.3.4",
|
|
63
|
+
"ulid": "^3.0.2",
|
|
64
|
+
"@ark-ui/react": "^5.37.2",
|
|
65
|
+
"@internationalized/date": "^3.12.0",
|
|
66
|
+
"@heroicons/react": "^2.2.0",
|
|
67
|
+
"d3": "^7.9.0",
|
|
68
|
+
"d3-sankey": "^0.12.3",
|
|
69
|
+
"recharts": "^3.8.1",
|
|
70
|
+
"player.js": "^0.1.0",
|
|
71
|
+
"tinycolor2": "^1.6.0",
|
|
72
|
+
"html-to-image": "^1.11.13",
|
|
73
|
+
"path-to-regexp": "^8.4.2",
|
|
74
|
+
"postcss": "^8.5.15",
|
|
75
|
+
"postcss-selector-parser": "^7.1.4"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@types/node": "^22.20.0",
|
|
79
|
+
"@types/react": "^19.2.17",
|
|
80
|
+
"@types/react-dom": "^19.2.3",
|
|
81
|
+
"@types/d3": "^7.4.3",
|
|
82
|
+
"@types/d3-sankey": "^0.12.5",
|
|
83
|
+
"prettier": "^3.8.4",
|
|
84
|
+
"prettier-plugin-astro": "^0.14.1",
|
|
85
|
+
"prettier-plugin-tailwindcss": "^0.7.4",
|
|
86
|
+
"typescript": "^5.9.3",
|
|
87
|
+
"@types/tinycolor2": "^1.4.6",
|
|
88
|
+
"@mhsdesign/jit-browser-tailwindcss": "^0.4.2"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
53
91
|
"dependencies": {
|
|
54
92
|
"@calcom/embed-react": "^1.5.3",
|
|
55
93
|
"kleur": "^4.1.5",
|
|
56
94
|
"prompts": "^2.4.2"
|
|
57
95
|
},
|
|
58
96
|
"devDependencies": {
|
|
59
|
-
"@ark-ui/react": "^5.
|
|
97
|
+
"@ark-ui/react": "^5.37.2",
|
|
60
98
|
"@astrojs/react": "^4.4.2",
|
|
61
|
-
"@heroicons/react": "^2.
|
|
62
|
-
"@internationalized/date": "^3.
|
|
99
|
+
"@heroicons/react": "^2.2.0",
|
|
100
|
+
"@internationalized/date": "^3.12.0",
|
|
63
101
|
"@mhsdesign/jit-browser-tailwindcss": "^0.4.2",
|
|
64
|
-
"@nanostores/persistent": "^1.3.
|
|
65
|
-
"@nanostores/react": "^1.
|
|
102
|
+
"@nanostores/persistent": "^1.3.4",
|
|
103
|
+
"@nanostores/react": "^1.1.0",
|
|
66
104
|
"@types/d3": "^7.4.3",
|
|
67
105
|
"@types/d3-sankey": "^0.12.5",
|
|
68
106
|
"@types/node": "^22.18.0",
|
|
@@ -74,7 +112,7 @@
|
|
|
74
112
|
"d3": "^7.9.0",
|
|
75
113
|
"d3-sankey": "^0.12.3",
|
|
76
114
|
"html-to-image": "^1.11.13",
|
|
77
|
-
"nanostores": "^1.
|
|
115
|
+
"nanostores": "^1.3.0",
|
|
78
116
|
"player.js": "^0.1.0",
|
|
79
117
|
"postcss": "^8.5.6",
|
|
80
118
|
"postcss-selector-parser": "^7.1.1",
|
|
@@ -96,8 +134,7 @@
|
|
|
96
134
|
"packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0",
|
|
97
135
|
"pnpm": {
|
|
98
136
|
"overrides": {
|
|
99
|
-
"esbuild@<=0.24.2": ">=0.25.0"
|
|
100
|
-
"@internationalized/date": "3.10.1"
|
|
137
|
+
"esbuild@<=0.24.2": ">=0.25.0"
|
|
101
138
|
}
|
|
102
139
|
}
|
|
103
140
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Static, hand-maintained per-install manifest of available codehook ids.
|
|
2
|
+
// The backend never sees this; it is a frontend build capability list only.
|
|
3
|
+
// To add/remove a hook, edit this array AND add/remove the matching blade at
|
|
4
|
+
// src/pages/codehooks/{id}.astro. Not regenerated automatically.
|
|
5
|
+
export const availableCodeHookIds: string[] = [
|
|
6
|
+
'featured-article',
|
|
7
|
+
'list-content',
|
|
8
|
+
'search-widget',
|
|
9
|
+
'bunny-video',
|
|
10
|
+
'epinet',
|
|
11
|
+
'shopify-product-grid',
|
|
12
|
+
'shopify-service-list',
|
|
13
|
+
];
|
|
@@ -2,6 +2,7 @@ import { useStore } from '@nanostores/react';
|
|
|
2
2
|
import { Dialog } from '@ark-ui/react/dialog';
|
|
3
3
|
import { Portal } from '@ark-ui/react/portal';
|
|
4
4
|
import { modalState } from '@/stores/shopify';
|
|
5
|
+
import { classNames } from '@/utils/helpers';
|
|
5
6
|
|
|
6
7
|
export default function CartModal() {
|
|
7
8
|
const state = useStore(modalState);
|
|
@@ -20,15 +21,24 @@ export default function CartModal() {
|
|
|
20
21
|
const isCartPage =
|
|
21
22
|
typeof window !== 'undefined' && window.location.pathname === '/cart';
|
|
22
23
|
|
|
24
|
+
const isMaxDurationRestriction =
|
|
25
|
+
state.type === 'restriction' && state.restrictionReason === 'maxDuration';
|
|
26
|
+
|
|
23
27
|
return (
|
|
24
28
|
<Dialog.Root
|
|
25
29
|
open={state.isOpen}
|
|
30
|
+
closeOnInteractOutside={false}
|
|
26
31
|
onOpenChange={(e) => !e.open && handleClose()}
|
|
27
32
|
>
|
|
28
33
|
<Portal>
|
|
29
34
|
<Dialog.Backdrop className="fixed inset-0 z-50 bg-black bg-opacity-75 backdrop-blur-sm" />
|
|
30
35
|
<Dialog.Positioner className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
|
31
|
-
<Dialog.Content
|
|
36
|
+
<Dialog.Content
|
|
37
|
+
className={classNames(
|
|
38
|
+
`w-full max-w-md overflow-hidden rounded-lg bg-white shadow-xl`,
|
|
39
|
+
isMaxDurationRestriction ? 'border-4 border-brand-3' : ''
|
|
40
|
+
)}
|
|
41
|
+
>
|
|
32
42
|
<div className="p-6">
|
|
33
43
|
<Dialog.Title className="text-xl font-bold text-gray-900">
|
|
34
44
|
{state.title}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
resetShopifyCommerceState,
|
|
4
|
+
transactionTraceId,
|
|
5
|
+
} from '@/stores/shopify';
|
|
6
|
+
import { bookingHelpers } from '@/utils/api/bookingHelpers';
|
|
7
|
+
|
|
8
|
+
export default function CartReset() {
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
let cancelled = false;
|
|
11
|
+
|
|
12
|
+
const run = async () => {
|
|
13
|
+
const traceId = transactionTraceId.get();
|
|
14
|
+
if (traceId) {
|
|
15
|
+
try {
|
|
16
|
+
await bookingHelpers.releaseHold(traceId);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.error('Failed to release hold during cart reset:', err);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (cancelled) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
resetShopifyCommerceState();
|
|
27
|
+
window.location.replace('/cart');
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
void run();
|
|
31
|
+
|
|
32
|
+
return () => {
|
|
33
|
+
cancelled = true;
|
|
34
|
+
};
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
return <p className="text-center text-sm text-gray-600">Resetting cart…</p>;
|
|
38
|
+
}
|
|
@@ -182,6 +182,7 @@ export default function ShopifyCartManager({
|
|
|
182
182
|
type: 'restriction',
|
|
183
183
|
title: 'Incompatible Booking Modes',
|
|
184
184
|
message: RESTRICTION_MESSAGES.INCOMPATIBLE_REMOTE,
|
|
185
|
+
restrictionReason: 'incompatibleRemote',
|
|
185
186
|
});
|
|
186
187
|
} else {
|
|
187
188
|
const rawDuration = calculateCartDuration(nextCart, resources);
|
|
@@ -196,6 +197,7 @@ export default function ShopifyCartManager({
|
|
|
196
197
|
type: 'restriction',
|
|
197
198
|
title: 'Appointment Length Limit Reached',
|
|
198
199
|
message: RESTRICTION_MESSAGES.MAX_DURATION(dynamicMax),
|
|
200
|
+
restrictionReason: 'maxDuration',
|
|
199
201
|
});
|
|
200
202
|
} else {
|
|
201
203
|
cartStore.set(nextCart);
|
|
@@ -6,7 +6,7 @@ import { getShopifyImage } from '@/utils/helpers';
|
|
|
6
6
|
import type { ResourceNode } from '@/types/compositorTypes';
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
|
-
resources:
|
|
9
|
+
resources: ResourceNode[];
|
|
10
10
|
options?: {
|
|
11
11
|
params?: {
|
|
12
12
|
options?: string;
|
|
@@ -218,9 +218,9 @@ function ProductCard({ resource, allServices }: ProductCardProps) {
|
|
|
218
218
|
|
|
219
219
|
const HEX_BG_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
220
220
|
|
|
221
|
-
export default function ShopifyProductGrid({ resources =
|
|
222
|
-
let products = resources
|
|
223
|
-
const services = resources
|
|
221
|
+
export default function ShopifyProductGrid({ resources = [], options }: Props) {
|
|
222
|
+
let products = resources.filter((r) => r.categorySlug === 'product');
|
|
223
|
+
const services = resources.filter((r) => r.categorySlug === 'service');
|
|
224
224
|
|
|
225
225
|
let group = '';
|
|
226
226
|
let title = '';
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { useStore } from '@nanostores/react';
|
|
2
|
+
import { Switch } from '@ark-ui/react/switch';
|
|
2
3
|
import { cartStore, addQueue, type CartAction } from '@/stores/shopify';
|
|
3
4
|
import {
|
|
4
5
|
getCartItemKey,
|
|
5
6
|
getServiceVariantIdFromCanonicalProduct,
|
|
6
7
|
isSharedFeeService,
|
|
7
8
|
} from '@/custom/shopify/shopifyHelpers';
|
|
9
|
+
import { classNames } from '@/utils/helpers';
|
|
8
10
|
import type { ResourceNode } from '@/types/compositorTypes';
|
|
9
11
|
|
|
10
12
|
interface Props {
|
|
11
|
-
resources:
|
|
13
|
+
resources: ResourceNode[];
|
|
12
14
|
options?: {
|
|
13
15
|
params?: {
|
|
14
16
|
options?: string;
|
|
@@ -18,11 +20,11 @@ interface Props {
|
|
|
18
20
|
|
|
19
21
|
const HEX_BG_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
20
22
|
|
|
21
|
-
export default function ShopifyServiceList({ resources =
|
|
23
|
+
export default function ShopifyServiceList({ resources = [], options }: Props) {
|
|
22
24
|
const cart = useStore(cartStore);
|
|
23
25
|
|
|
24
|
-
const products = resources
|
|
25
|
-
let services = resources
|
|
26
|
+
const products = resources.filter((r) => r.categorySlug === 'product');
|
|
27
|
+
let services = resources.filter((r) => r.categorySlug === 'service');
|
|
26
28
|
|
|
27
29
|
let group = '';
|
|
28
30
|
let title = '';
|
|
@@ -51,12 +53,12 @@ export default function ShopifyServiceList({ resources = {}, options }: Props) {
|
|
|
51
53
|
.filter((s): s is string => !!s)
|
|
52
54
|
);
|
|
53
55
|
|
|
54
|
-
const displayServices = services
|
|
55
|
-
(s) => !boundServiceSlugs.has(s.slug)
|
|
56
|
-
|
|
56
|
+
const displayServices = services
|
|
57
|
+
.filter((s) => !boundServiceSlugs.has(s.slug))
|
|
58
|
+
.sort((a, b) => a.title.localeCompare(b.title));
|
|
57
59
|
|
|
58
|
-
const
|
|
59
|
-
const actionType =
|
|
60
|
+
const handleCheckedChange = (resource: ResourceNode, checked: boolean) => {
|
|
61
|
+
const actionType = checked ? 'add' : 'remove';
|
|
60
62
|
const gid =
|
|
61
63
|
typeof resource.optionsPayload?.gid === 'string'
|
|
62
64
|
? resource.optionsPayload.gid
|
|
@@ -116,47 +118,58 @@ export default function ShopifyServiceList({ resources = {}, options }: Props) {
|
|
|
116
118
|
const duration = resource.optionsPayload?.bookingLengthMinutes;
|
|
117
119
|
|
|
118
120
|
return (
|
|
119
|
-
<
|
|
121
|
+
<Switch.Root
|
|
120
122
|
key={resource.id}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
checked={isSelected}
|
|
124
|
+
onCheckedChange={(details) =>
|
|
125
|
+
handleCheckedChange(resource, details.checked)
|
|
126
|
+
}
|
|
127
|
+
className="block w-full"
|
|
126
128
|
>
|
|
127
|
-
<div
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
<div
|
|
130
|
+
className={`flex cursor-pointer items-center justify-between rounded-lg border p-4 transition-colors ${
|
|
131
|
+
isSelected
|
|
132
|
+
? 'border-black bg-gray-50'
|
|
133
|
+
: 'border-gray-200 bg-white hover:border-gray-300'
|
|
134
|
+
}`}
|
|
135
|
+
>
|
|
136
|
+
<Switch.Label className="min-w-0 flex-grow cursor-pointer">
|
|
137
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
138
|
+
<h3 className="font-bold text-gray-900">
|
|
139
|
+
{resource.title}
|
|
140
|
+
</h3>
|
|
141
|
+
{duration ? (
|
|
142
|
+
<span className="inline-flex items-center rounded-sm bg-blue-50 px-2 py-0.5 text-xs font-bold text-blue-700">
|
|
143
|
+
{duration} mins
|
|
144
|
+
</span>
|
|
145
|
+
) : null}
|
|
146
|
+
{isSelected ? (
|
|
147
|
+
<span className="inline-flex items-center rounded-sm bg-gray-100 px-2 py-0.5 text-xs font-bold text-gray-800">
|
|
148
|
+
In Cart
|
|
149
|
+
</span>
|
|
150
|
+
) : null}
|
|
151
|
+
</div>
|
|
152
|
+
<p className="mt-1 text-sm text-gray-500">
|
|
153
|
+
{resource.oneliner}
|
|
154
|
+
</p>
|
|
155
|
+
</Switch.Label>
|
|
156
|
+
|
|
157
|
+
<Switch.Control
|
|
158
|
+
className={classNames(
|
|
159
|
+
`relative ml-4 inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none`,
|
|
160
|
+
isSelected ? 'bg-brand-5' : 'bg-gray-200'
|
|
136
161
|
)}
|
|
137
|
-
</div>
|
|
138
|
-
<p className="mt-1 text-sm text-gray-500">
|
|
139
|
-
{resource.oneliner}
|
|
140
|
-
</p>
|
|
141
|
-
</div>
|
|
142
|
-
|
|
143
|
-
<div className="ml-4 flex-shrink-0">
|
|
144
|
-
<button
|
|
145
|
-
onClick={() => handleToggle(resource, selectedQuantity)}
|
|
146
|
-
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${
|
|
147
|
-
isSelected ? 'bg-black' : 'bg-gray-200'
|
|
148
|
-
}`}
|
|
149
|
-
role="switch"
|
|
150
|
-
aria-checked={isSelected}
|
|
151
162
|
>
|
|
152
|
-
<
|
|
153
|
-
className={
|
|
163
|
+
<Switch.Thumb
|
|
164
|
+
className={classNames(
|
|
165
|
+
`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out`,
|
|
154
166
|
isSelected ? 'translate-x-5' : 'translate-x-0'
|
|
155
|
-
}
|
|
167
|
+
)}
|
|
156
168
|
/>
|
|
157
|
-
</
|
|
169
|
+
</Switch.Control>
|
|
170
|
+
<Switch.HiddenInput />
|
|
158
171
|
</div>
|
|
159
|
-
</
|
|
172
|
+
</Switch.Root>
|
|
160
173
|
);
|
|
161
174
|
})}
|
|
162
175
|
</div>
|