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.
Files changed (55) hide show
  1. package/bin/create-tractstack.js +38 -59
  2. package/dist/index.js +68 -36
  3. package/package.json +46 -9
  4. package/templates/custom/minimal/codehooks.ts +13 -0
  5. package/templates/custom/shopify/CartModal.tsx +11 -1
  6. package/templates/custom/shopify/CartReset.tsx +38 -0
  7. package/templates/custom/shopify/ShopifyCartManager.tsx +2 -0
  8. package/templates/custom/shopify/ShopifyProductGrid.tsx +4 -4
  9. package/templates/custom/shopify/ShopifyServiceList.tsx +56 -43
  10. package/templates/custom/shopify/cart-reset.astro +19 -0
  11. package/templates/custom/with-examples/codehooks.ts +15 -0
  12. package/templates/src/components/Header.astro +1 -1
  13. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +38 -23
  14. package/templates/src/components/codehooks/EpinetTableView.tsx +5 -2
  15. package/templates/src/components/codehooks/EpinetWrapper.tsx +10 -5
  16. package/templates/src/components/codehooks/FeaturedArticle.astro +3 -3
  17. package/templates/src/components/codehooks/ListContent.astro +3 -3
  18. package/templates/src/components/compositor/Node.tsx +13 -2
  19. package/templates/src/components/compositor/nodes/Pane.tsx +2 -14
  20. package/templates/src/components/edit/pane/AddPanePanel.tsx +3 -2
  21. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +35 -14
  22. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -1
  23. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +2 -2
  24. package/templates/src/components/storykeep/Dashboard_Analytics.tsx +8 -4
  25. package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +5 -2
  26. package/templates/src/components/storykeep/state/FetchAnalytics.tsx +8 -4
  27. package/templates/src/components/storykeep/widgets/Wizard.tsx +4 -2
  28. package/templates/src/lib/codeHookHelper.ts +156 -0
  29. package/templates/src/lib/resources.ts +41 -0
  30. package/templates/src/lib/storyData.ts +1 -2
  31. package/templates/src/pages/[...slug]/edit.astro +3 -3
  32. package/templates/src/pages/[...slug].astro +76 -70
  33. package/templates/src/pages/codehooks/[...hookId].astro +18 -0
  34. package/templates/src/pages/codehooks/bunny-video.astro +9 -0
  35. package/templates/src/pages/codehooks/custom-hero.astro +6 -0
  36. package/templates/src/pages/codehooks/epinet.astro +15 -0
  37. package/templates/src/pages/codehooks/featured-article.astro +13 -0
  38. package/templates/src/pages/codehooks/get-crafting.astro +8 -0
  39. package/templates/src/pages/codehooks/list-content.astro +13 -0
  40. package/templates/src/pages/codehooks/search-widget.astro +13 -0
  41. package/templates/src/pages/codehooks/shopify-product-grid.astro +23 -0
  42. package/templates/src/pages/codehooks/shopify-service-list.astro +23 -0
  43. package/templates/src/pages/context/[...contextSlug]/edit.astro +3 -3
  44. package/templates/src/pages/context/[...contextSlug].astro +47 -10
  45. package/templates/src/pages/sandbox.astro +3 -14
  46. package/templates/src/stores/analytics.ts +77 -107
  47. package/templates/src/stores/shopify.ts +34 -4
  48. package/utils/inject-files.ts +70 -37
  49. package/templates/custom/minimal/CodeHook.astro +0 -72
  50. package/templates/custom/with-examples/CodeHook.astro +0 -81
  51. package/templates/custom/with-examples/ProductCard.astro +0 -29
  52. package/templates/custom/with-examples/ProductCardWrapper.astro +0 -43
  53. package/templates/custom/with-examples/ProductGrid.astro +0 -64
  54. package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -157
  55. package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -279
@@ -1,6 +1,12 @@
1
1
  ---
2
2
  import Layout from '@/layouts/Layout.astro';
3
3
  import { getBrandConfig } from '@/utils/api/brandConfig';
4
+ import { getFullContentMap } from '@/stores/analytics';
5
+ import {
6
+ buildCodeHookBladePath,
7
+ fetchCodeHookBladeHtml,
8
+ resolveSSRFetchOrigin,
9
+ } from '@/lib/codeHookHelper';
4
10
  import { handleFailedResponse } from '@/utils/backend';
5
11
  import { preHealthCheck } from '@/utils/backend';
6
12
 
@@ -56,6 +62,8 @@ try {
56
62
 
57
63
  const paneId = contextPaneData.id;
58
64
  const paneTitle = contextPaneData.title || 'Context';
65
+ const codeHookTarget = contextPaneData.codeHookTarget;
66
+ const codeHookOptions = contextPaneData.codeHookPayload?.options;
59
67
 
60
68
  // Get rendered fragment for the context pane
61
69
  let fragmentData = '';
@@ -94,6 +102,23 @@ const brandConfig = await getBrandConfig(tenantId);
94
102
  if (!brandConfig.SITE_INIT) {
95
103
  return Astro.redirect('/storykeep');
96
104
  }
105
+
106
+ // Warm the in-process content-map store before SSR-fetching the codehook blade.
107
+ await getFullContentMap(tenantId);
108
+
109
+ let codeHookBladeHtml = '';
110
+ if (codeHookTarget) {
111
+ const bladePath = buildCodeHookBladePath(codeHookTarget, {
112
+ paneId,
113
+ tenantId,
114
+ optionsStr: codeHookOptions || '',
115
+ });
116
+ codeHookBladeHtml = await fetchCodeHookBladeHtml(
117
+ bladePath,
118
+ Astro.request,
119
+ resolveSSRFetchOrigin(Astro.url, brandConfig.SITE_URL)
120
+ );
121
+ }
97
122
  ---
98
123
 
99
124
  <Layout
@@ -108,16 +133,28 @@ if (!brandConfig.SITE_INIT) {
108
133
  storyfragmentId={paneId}
109
134
  >
110
135
  <main id="main-content" class="w-full">
111
- <div
112
- id={`pane-${paneId}`}
113
- data-pane-id={paneId}
114
- class="context-pane-container"
115
- hx-get={`/api/v1/fragments/panes/${paneId}`}
116
- hx-trigger="refresh"
117
- hx-swap="innerHTML scroll:none"
118
- >
119
- <Fragment set:html={fragmentData} />
120
- </div>
136
+ {
137
+ codeHookTarget ? (
138
+ <div
139
+ id={`pane-${paneId}`}
140
+ data-pane-id={paneId}
141
+ class="context-pane-container"
142
+ >
143
+ <Fragment set:html={codeHookBladeHtml} />
144
+ </div>
145
+ ) : (
146
+ <div
147
+ id={`pane-${paneId}`}
148
+ data-pane-id={paneId}
149
+ class="context-pane-container"
150
+ hx-get={`/api/v1/fragments/panes/${paneId}`}
151
+ hx-trigger="refresh"
152
+ hx-swap="innerHTML scroll:none"
153
+ >
154
+ <Fragment set:html={fragmentData} />
155
+ </div>
156
+ )
157
+ }
121
158
 
122
159
  <div class="py-12 text-center text-2xl md:text-3xl">
123
160
  <button
@@ -5,9 +5,7 @@ import Layout from '@/layouts/Layout.astro';
5
5
  import Header from '@/components/Header.astro';
6
6
  import { getFullContentMap } from '@/stores/analytics';
7
7
  import { getBrandConfig } from '@/utils/api/brandConfig';
8
- import CodeHook, {
9
- components as codeHookComponents,
10
- } from '@/custom/CodeHook.astro';
8
+ import { availableCodeHookIds } from '@/custom/codehooks';
11
9
  import StoryKeepHeader from '@/components/edit/Header';
12
10
  import StoryKeepToolBar from '@/components/edit/ToolBar';
13
11
  import StoryKeepToolMode from '@/components/edit/ToolMode';
@@ -64,15 +62,6 @@ const sandboxToken = `${timestamp}.${signature}`;
64
62
  isStoryKeep={true}
65
63
  isEditor={true}
66
64
  >
67
- <CodeHook
68
- target="get-tractstack"
69
- options={{
70
- params: {
71
- options: JSON.stringify({ isEmbedded: false }),
72
- },
73
- }}
74
- />
75
-
76
65
  <Header
77
66
  title={title}
78
67
  slug="sandbox"
@@ -115,7 +104,7 @@ const sandboxToken = `${timestamp}.${signature}`;
115
104
  fullContentMap={fullContentMap}
116
105
  fullCanonicalURL="/sandbox"
117
106
  urlParams={urlParams}
118
- availableCodeHooks={Object.keys(codeHookComponents)}
107
+ availableCodeHooks={availableCodeHookIds}
119
108
  isSandboxMode={true}
120
109
  sandboxToken={sandboxToken}
121
110
  client:only="react"
@@ -137,7 +126,7 @@ const sandboxToken = `${timestamp}.${signature}`;
137
126
 
138
127
  <div class="pointer-events-auto max-h-full">
139
128
  <SettingsPanel
140
- availableCodeHooks={Object.keys(codeHookComponents)}
129
+ availableCodeHooks={availableCodeHookIds}
141
130
  client:only="react"
142
131
  />
143
132
  </div>
@@ -1,4 +1,4 @@
1
- import { atom } from 'nanostores';
1
+ import { atom, computed } from 'nanostores';
2
2
  import { TractStackAPI } from '@/utils/api';
3
3
 
4
4
  interface AvailableFilter {
@@ -11,43 +11,38 @@ export interface AppliedFilter {
11
11
  value: string;
12
12
  }
13
13
 
14
- const tenantEpinetCustomFilters = atom<
15
- Record<
14
+ export interface EpinetFiltersState {
15
+ enabled: boolean;
16
+ visitorType: 'all' | 'anonymous' | 'known';
17
+ selectedUserId: string | null;
18
+ startTimeUTC: string | null;
19
+ endTimeUTC: string | null;
20
+ userCounts: Array<{ id: string; count: number; isKnown: boolean }>;
21
+ hourlyNodeActivity: Record<
16
22
  string,
17
- {
18
- enabled: boolean;
19
- visitorType: 'all' | 'anonymous' | 'known';
20
- selectedUserId: string | null;
21
- startTimeUTC: string | null;
22
- endTimeUTC: string | null;
23
- userCounts: Array<{ id: string; count: number; isKnown: boolean }>;
24
- hourlyNodeActivity: Record<
25
- string,
26
- Record<
27
- string,
28
- {
29
- events: Record<string, number>;
30
- visitorIds: string[];
31
- }
32
- >
33
- >;
34
- availableFilters: AvailableFilter[];
35
- appliedFilters: AppliedFilter[];
36
- }
37
- >
38
- >({});
23
+ Record<
24
+ string,
25
+ {
26
+ events: Record<string, number>;
27
+ visitorIds: string[];
28
+ }
29
+ >
30
+ >;
31
+ availableFilters: AvailableFilter[];
32
+ appliedFilters: AppliedFilter[];
33
+ }
39
34
 
40
- const tenantFullContentMaps = atom<
41
- Record<
42
- string,
43
- {
44
- data: any[];
45
- lastUpdated: number;
46
- }
47
- >
48
- >({});
35
+ export interface TenantFullContentMapState {
36
+ data: any[];
37
+ lastUpdated: number;
38
+ }
39
+
40
+ const tenantEpinetCustomFilters = atom<Record<string, EpinetFiltersState>>({});
41
+
42
+ const tenantFullContentMaps = atom<Record<string, TenantFullContentMapState>>(
43
+ {}
44
+ );
49
45
 
50
- // Helper to get current tenant ID
51
46
  function getCurrentTenantId(): string {
52
47
  const resolvedTenantId =
53
48
  (typeof window !== 'undefined' && window.TRACTSTACK_CONFIG?.tenantId) ||
@@ -56,9 +51,9 @@ function getCurrentTenantId(): string {
56
51
  return resolvedTenantId;
57
52
  }
58
53
 
59
- const defaultEpinetFilters = {
54
+ const defaultEpinetFilters: EpinetFiltersState = {
60
55
  enabled: false,
61
- visitorType: 'all' as 'all' | 'anonymous' | 'known',
56
+ visitorType: 'all',
62
57
  selectedUserId: null,
63
58
  startTimeUTC: null,
64
59
  endTimeUTC: null,
@@ -68,81 +63,56 @@ const defaultEpinetFilters = {
68
63
  appliedFilters: [],
69
64
  };
70
65
 
71
- const createEpinetFiltersStore = () => {
72
- const store = {
73
- get: () => {
74
- const tenantId = getCurrentTenantId();
75
- return tenantEpinetCustomFilters.get()[tenantId] || defaultEpinetFilters;
76
- },
77
-
78
- set: (tenantId: string, updates: any) => {
79
- const currentFilters =
80
- tenantEpinetCustomFilters.get()[tenantId] || defaultEpinetFilters;
81
- tenantEpinetCustomFilters.set({
82
- ...tenantEpinetCustomFilters.get(),
83
- [tenantId]: {
84
- ...currentFilters,
85
- ...updates,
86
- },
87
- });
88
- },
89
-
90
- subscribe: (callback: (value: any) => void) => {
91
- const tenantId = getCurrentTenantId();
92
- return tenantEpinetCustomFilters.subscribe((filters) => {
93
- callback(filters[tenantId] || defaultEpinetFilters);
94
- });
95
- },
96
- lc: 0,
97
- listen: function (callback: any) {
98
- return this.subscribe(callback);
99
- },
100
- notify: function () {},
101
- off: function () {},
102
- get value() {
103
- return this.get();
104
- },
105
- };
106
-
107
- return store;
108
- };
109
-
110
- const createFullContentMapStore = () => {
111
- const store = {
112
- get: () => {
113
- const tenantId = getCurrentTenantId();
114
- return tenantFullContentMaps.get()[tenantId] || null;
115
- },
66
+ export const epinetCustomFilters = computed(
67
+ tenantEpinetCustomFilters,
68
+ (filters) => {
69
+ const tenantId = getCurrentTenantId();
70
+ return filters[tenantId] || defaultEpinetFilters;
71
+ }
72
+ );
116
73
 
117
- set: (tenantId: string, data: { data: any[]; lastUpdated: number }) => {
118
- tenantFullContentMaps.set({
119
- ...tenantFullContentMaps.get(),
120
- [tenantId]: data,
121
- });
122
- },
74
+ export function getEpinetCustomFilters(): EpinetFiltersState {
75
+ return epinetCustomFilters.get();
76
+ }
123
77
 
124
- subscribe: (callback: (value: any) => void) => {
125
- const tenantId = getCurrentTenantId();
126
- return tenantFullContentMaps.subscribe((maps) => {
127
- callback(maps[tenantId] || null);
128
- });
129
- },
130
- lc: 0,
131
- listen: function (callback: any) {
132
- return this.subscribe(callback);
133
- },
134
- notify: function () {},
135
- off: function () {},
136
- get value() {
137
- return this.get();
78
+ export function setEpinetCustomFilters(
79
+ tenantId: string,
80
+ updates: Partial<EpinetFiltersState>
81
+ ): void {
82
+ const currentFilters =
83
+ tenantEpinetCustomFilters.get()[tenantId] || defaultEpinetFilters;
84
+ tenantEpinetCustomFilters.set({
85
+ ...tenantEpinetCustomFilters.get(),
86
+ [tenantId]: {
87
+ ...currentFilters,
88
+ ...updates,
138
89
  },
139
- };
90
+ });
91
+ }
140
92
 
141
- return store;
142
- };
93
+ export const fullContentMapStore = computed(tenantFullContentMaps, (maps) => {
94
+ const tenantId = getCurrentTenantId();
95
+ return maps[tenantId] || null;
96
+ });
97
+
98
+ export function setTenantFullContentMap(
99
+ tenantId: string,
100
+ data: TenantFullContentMapState
101
+ ): void {
102
+ tenantFullContentMaps.set({
103
+ ...tenantFullContentMaps.get(),
104
+ [tenantId]: data,
105
+ });
106
+ }
143
107
 
144
- export const epinetCustomFilters = createEpinetFiltersStore();
145
- export const fullContentMapStore = createFullContentMapStore();
108
+ // Synchronous in-process accessor for the warm content-map atom, keyed on an
109
+ // explicit tenantId (unlike fullContentMapStore.get(), which keys on
110
+ // getCurrentTenantId() and resolves the wrong tenant during SSR sub-requests).
111
+ // Returns [] on a cold miss; codehook blades fall back to the async
112
+ // getFullContentMap(tenantId) when this is empty.
113
+ export function getCachedFullContentMap(tenantId: string): any[] {
114
+ return tenantFullContentMaps.get()[tenantId]?.data ?? [];
115
+ }
146
116
 
147
117
  export async function getFullContentMap(tenantId: string): Promise<any[]> {
148
118
  const api = new TractStackAPI(tenantId);
@@ -26,6 +26,7 @@ export const modalState = atom<{
26
26
  type: 'success' | 'restriction';
27
27
  title: string;
28
28
  message: string;
29
+ restrictionReason?: 'maxDuration' | 'incompatibleRemote';
29
30
  }>({
30
31
  isOpen: false,
31
32
  type: 'success',
@@ -258,16 +259,45 @@ export function setCustomerDetails(details: Partial<CustomerDetails>) {
258
259
  });
259
260
  }
260
261
 
261
- export function clearCommerceState() {
262
+ const SHOPIFY_PERSISTENCE_KEYS = [
263
+ 'tractstack_shopify_queue',
264
+ 'tractstack_shopify_cart',
265
+ 'tractstack_shopify_queue_state',
266
+ 'tractstack_shopify_cart_state',
267
+ 'tractstack_shopify_trace_id',
268
+ 'tractstack_shopify_appointment_mode',
269
+ 'tractstack_shopify_customer',
270
+ ] as const;
271
+
272
+ export function resetShopifyCommerceState() {
273
+ addQueue.set([]);
262
274
  cartStore.set({});
275
+ cartState.set(CART_STATES.READY);
276
+ queueState.set(QUEUE_STATES.READY);
277
+ transactionTraceId.set('');
278
+ preferredAppointmentMode.set('IN_PERSON');
263
279
  customerDetails.set({
264
280
  name: '',
265
281
  email: '',
266
282
  leadId: '',
267
283
  });
268
- transactionTraceId.set('');
269
- preferredAppointmentMode.set('IN_PERSON');
270
- cartState.set(CART_STATES.READY);
284
+ modalState.set({
285
+ isOpen: false,
286
+ type: 'success',
287
+ title: '',
288
+ message: '',
289
+ restrictionReason: undefined,
290
+ });
291
+
292
+ if (typeof localStorage !== 'undefined') {
293
+ SHOPIFY_PERSISTENCE_KEYS.forEach((key) => {
294
+ localStorage.removeItem(key);
295
+ });
296
+ }
297
+ }
298
+
299
+ export function clearCommerceState() {
300
+ resetShopifyCommerceState();
271
301
  }
272
302
 
273
303
  export interface CartKeyParams {
@@ -900,6 +900,14 @@ export async function injectTemplateFiles(
900
900
  src: resolve('../templates/custom/shopify/cart.astro'),
901
901
  dest: 'src/pages/cart.astro',
902
902
  },
903
+ {
904
+ src: resolve('../templates/custom/shopify/cart-reset.astro'),
905
+ dest: 'src/pages/cart/reset.astro',
906
+ },
907
+ {
908
+ src: resolve('../templates/custom/shopify/CartReset.tsx'),
909
+ dest: 'src/custom/shopify/CartReset.tsx',
910
+ },
903
911
  {
904
912
  src: resolve('../templates/src/pages/privacy.astro'),
905
913
  dest: 'src/pages/privacy.astro',
@@ -1518,18 +1526,6 @@ export async function injectTemplateFiles(
1518
1526
  ),
1519
1527
  dest: 'src/components/codehooks/ListContentSetup.tsx',
1520
1528
  },
1521
- {
1522
- src: resolve(
1523
- '../templates/src/components/codehooks/ProductCardSetup.tsx'
1524
- ),
1525
- dest: 'src/components/codehooks/ProductCardSetup.tsx',
1526
- },
1527
- {
1528
- src: resolve(
1529
- '../templates/src/components/codehooks/ProductGridSetup.tsx'
1530
- ),
1531
- dest: 'src/components/codehooks/ProductGridSetup.tsx',
1532
- },
1533
1529
  {
1534
1530
  src: resolve(
1535
1531
  '../templates/src/components/codehooks/BunnyVideoWrapper.astro'
@@ -1541,6 +1537,32 @@ export async function injectTemplateFiles(
1541
1537
  dest: 'src/components/codehooks/BunnyVideoSetup.tsx',
1542
1538
  },
1543
1539
 
1540
+ // CodeHook Blades (partial routes; core hooks overwritable)
1541
+ {
1542
+ src: resolve('../templates/src/pages/codehooks/featured-article.astro'),
1543
+ dest: 'src/pages/codehooks/featured-article.astro',
1544
+ },
1545
+ {
1546
+ src: resolve('../templates/src/pages/codehooks/list-content.astro'),
1547
+ dest: 'src/pages/codehooks/list-content.astro',
1548
+ },
1549
+ {
1550
+ src: resolve('../templates/src/pages/codehooks/search-widget.astro'),
1551
+ dest: 'src/pages/codehooks/search-widget.astro',
1552
+ },
1553
+ {
1554
+ src: resolve('../templates/src/pages/codehooks/bunny-video.astro'),
1555
+ dest: 'src/pages/codehooks/bunny-video.astro',
1556
+ },
1557
+ {
1558
+ src: resolve('../templates/src/pages/codehooks/epinet.astro'),
1559
+ dest: 'src/pages/codehooks/epinet.astro',
1560
+ },
1561
+ {
1562
+ src: resolve('../templates/src/pages/codehooks/[...hookId].astro'),
1563
+ dest: 'src/pages/codehooks/[...hookId].astro',
1564
+ },
1565
+
1544
1566
  // Widget Components
1545
1567
  {
1546
1568
  src: resolve('../templates/src/components/widgets/Impression.tsx'),
@@ -1568,6 +1590,10 @@ export async function injectTemplateFiles(
1568
1590
  src: resolve('../templates/src/lib/resources.ts'),
1569
1591
  dest: 'src/lib/resources.ts',
1570
1592
  },
1593
+ {
1594
+ src: resolve('../templates/src/lib/codeHookHelper.ts'),
1595
+ dest: 'src/lib/codeHookHelper.ts',
1596
+ },
1571
1597
 
1572
1598
  // Client Scripts
1573
1599
  {
@@ -2357,15 +2383,6 @@ export async function injectTemplateFiles(
2357
2383
  },
2358
2384
 
2359
2385
  // Custom Components (Conditional)
2360
- {
2361
- src: resolve(
2362
- config?.includeExamples
2363
- ? '../templates/custom/with-examples/CodeHook.astro'
2364
- : '../templates/custom/minimal/CodeHook.astro'
2365
- ),
2366
- dest: 'src/custom/CodeHook.astro',
2367
- protected: true,
2368
- },
2369
2386
  {
2370
2387
  src: resolve(
2371
2388
  config?.includeExamples
@@ -2376,11 +2393,7 @@ export async function injectTemplateFiles(
2376
2393
  protected: true,
2377
2394
  },
2378
2395
  {
2379
- src: resolve(
2380
- config?.includeExamples
2381
- ? '../templates/custom/with-examples/HeaderWidget.astro'
2382
- : '../templates/custom/minimal/HeaderWidget.astro'
2383
- ),
2396
+ src: resolve('../templates/custom/minimal/HeaderWidget.astro'),
2384
2397
  dest: 'src/custom/HeaderWidget.astro',
2385
2398
  protected: true,
2386
2399
  },
@@ -2450,6 +2463,33 @@ export async function injectTemplateFiles(
2450
2463
  protected: true,
2451
2464
  },
2452
2465
 
2466
+ // CodeHook manifest (frontend capability list; hand-maintained, protected)
2467
+ {
2468
+ src: resolve(
2469
+ config?.includeExamples
2470
+ ? '../templates/custom/with-examples/codehooks.ts'
2471
+ : '../templates/custom/minimal/codehooks.ts'
2472
+ ),
2473
+ dest: 'src/custom/codehooks.ts',
2474
+ protected: true,
2475
+ },
2476
+
2477
+ // CodeHook Blades (userland hooks; protected once installed)
2478
+ {
2479
+ src: resolve(
2480
+ '../templates/src/pages/codehooks/shopify-product-grid.astro'
2481
+ ),
2482
+ dest: 'src/pages/codehooks/shopify-product-grid.astro',
2483
+ protected: true,
2484
+ },
2485
+ {
2486
+ src: resolve(
2487
+ '../templates/src/pages/codehooks/shopify-service-list.astro'
2488
+ ),
2489
+ dest: 'src/pages/codehooks/shopify-service-list.astro',
2490
+ protected: true,
2491
+ },
2492
+
2453
2493
  // Example Components (Conditional)
2454
2494
  ...(config?.includeExamples
2455
2495
  ? [
@@ -2466,20 +2506,13 @@ export async function injectTemplateFiles(
2466
2506
  protected: true,
2467
2507
  },
2468
2508
  {
2469
- src: resolve('../templates/custom/with-examples/ProductGrid.astro'),
2470
- dest: 'src/custom/ProductGrid.astro',
2471
- protected: true,
2472
- },
2473
- {
2474
- src: resolve(
2475
- '../templates/custom/with-examples/ProductCardWrapper.astro'
2476
- ),
2477
- dest: 'src/custom/ProductCardWrapper.astro',
2509
+ src: resolve('../templates/src/pages/codehooks/custom-hero.astro'),
2510
+ dest: 'src/pages/codehooks/custom-hero.astro',
2478
2511
  protected: true,
2479
2512
  },
2480
2513
  {
2481
- src: resolve('../templates/custom/with-examples/ProductCard.astro'),
2482
- dest: 'src/custom/ProductCard.astro',
2514
+ src: resolve('../templates/src/pages/codehooks/get-crafting.astro'),
2515
+ dest: 'src/pages/codehooks/get-crafting.astro',
2483
2516
  protected: true,
2484
2517
  },
2485
2518
  {
@@ -1,72 +0,0 @@
1
- ---
2
- import FeaturedArticle from '@/components/codehooks/FeaturedArticle.astro';
3
- import ListContent from '@/components/codehooks/ListContent.astro';
4
- import SearchWidget from '@/components/codehooks/SearchWidget.tsx';
5
- import BunnyVideoWrapper from '@/components/codehooks/BunnyVideoWrapper.astro';
6
- import EpinetWrapper from '@/components/codehooks/EpinetWrapper';
7
- import ShopifyProductGrid from '@/custom/shopify/ShopifyProductGrid';
8
- import ShopifyServiceList from '@/custom/shopify/ShopifyServiceList';
9
- import type { FullContentMapItem } from '@/types/tractstack';
10
- import type { ResourceNode } from '@/types/compositorTypes';
11
- // import CustomHero from './CustomHero.astro';
12
- // Uncomment to add custom components
13
-
14
- export interface Props {
15
- target: string;
16
- paneId: string;
17
- fullContentMap: FullContentMapItem[];
18
- resourcesPayload?: Record<string, ResourceNode[]>;
19
- options?: {
20
- params?: {
21
- options?: string;
22
- };
23
- };
24
- }
25
-
26
- const { target, options, fullContentMap, resourcesPayload = {} } = Astro.props;
27
-
28
- export const components = {
29
- 'featured-article': true,
30
- 'featured-content': true,
31
- 'list-content': true,
32
- 'search-widget': true,
33
- 'bunny-video': import.meta.env.PUBLIC_ENABLE_BUNNY === 'true',
34
- epinet: true,
35
- 'shopify-product-grid': true,
36
- 'shopify-service-list': true,
37
- // "custom-hero": true, // Uncomment when you create CustomHero.astro
38
- };
39
- ---
40
-
41
- {
42
- target === 'list-content' ? (
43
- <ListContent options={options} contentMap={fullContentMap} />
44
- ) : target === 'featured-article' ? (
45
- <FeaturedArticle options={options} contentMap={fullContentMap} />
46
- ) : target === 'search-widget' ? (
47
- <SearchWidget fullContentMap={fullContentMap} client:load />
48
- ) : target === 'bunny-video' && import.meta.env.PUBLIC_ENABLE_BUNNY ? (
49
- <BunnyVideoWrapper options={options} />
50
- ) : target === 'epinet' ? (
51
- <EpinetWrapper fullContentMap={fullContentMap} client:only="react" />
52
- ) : target === 'shopify-product-grid' ? (
53
- <ShopifyProductGrid
54
- options={options}
55
- resources={resourcesPayload}
56
- client:only="react"
57
- />
58
- ) : target === 'shopify-service-list' ? (
59
- <ShopifyServiceList
60
- options={options}
61
- resources={resourcesPayload}
62
- client:only="react"
63
- />
64
- ) : (
65
- /* : target === "custom-hero" ? (
66
- <CustomHero />
67
- ) */
68
- <div class="rounded-lg bg-gray-50 p-8 text-center">
69
- <p class="text-gray-600">CodeHook target "{target}" not found</p>
70
- </div>
71
- )
72
- }