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
@@ -0,0 +1,156 @@
1
+ import { getCachedFullContentMap, getFullContentMap } from '@/stores/analytics';
2
+ import { getCodeHookResources } from '@/lib/resources';
3
+ import type { ResourceNode } from '@/types/compositorTypes';
4
+
5
+ export interface CodeHookBladeContext {
6
+ tenantId: string;
7
+ paneId: string;
8
+ optionsStr: string;
9
+ options?: { params: { options: string } };
10
+ }
11
+
12
+ export interface CodeHookResourceFilters {
13
+ categories: string[];
14
+ slugs: string[];
15
+ }
16
+
17
+ export function parseCodeHookBladeContext(
18
+ searchParams: URLSearchParams
19
+ ): CodeHookBladeContext {
20
+ const tenantId = searchParams.get('tenantId') || 'default';
21
+ const paneId = searchParams.get('paneId') || '';
22
+ const optionsStr = searchParams.get('options') || '';
23
+ const options = optionsStr ? { params: { options: optionsStr } } : undefined;
24
+ return { tenantId, paneId, optionsStr, options };
25
+ }
26
+
27
+ export async function resolveCodeHookFullContentMap(
28
+ tenantId: string
29
+ ): Promise<any[]> {
30
+ let fullContentMap = getCachedFullContentMap(tenantId);
31
+ if (!fullContentMap.length) {
32
+ fullContentMap = await getFullContentMap(tenantId);
33
+ }
34
+ return fullContentMap;
35
+ }
36
+
37
+ export function parseCodeHookResourceFilters(
38
+ optionsStr: string,
39
+ logLabel?: string
40
+ ): CodeHookResourceFilters {
41
+ const categories: string[] = [];
42
+ const slugs: string[] = [];
43
+ if (!optionsStr) {
44
+ return { categories, slugs };
45
+ }
46
+ try {
47
+ const parsed = JSON.parse(optionsStr);
48
+ if (typeof parsed.category === 'string' && parsed.category) {
49
+ categories.push(...parsed.category.split('|'));
50
+ }
51
+ if (typeof parsed.slugs === 'string' && parsed.slugs) {
52
+ slugs.push(...parsed.slugs.split(','));
53
+ }
54
+ if (typeof parsed.slug === 'string' && parsed.slug) {
55
+ slugs.push(parsed.slug);
56
+ }
57
+ } catch (e) {
58
+ console.error(`Invalid options for ${logLabel ?? 'codehook resources'}`, e);
59
+ }
60
+ return { categories, slugs };
61
+ }
62
+
63
+ export async function resolveCodeHookResources(
64
+ tenantId: string,
65
+ optionsStr: string,
66
+ logLabel?: string
67
+ ): Promise<ResourceNode[]> {
68
+ const { categories, slugs } = parseCodeHookResourceFilters(
69
+ optionsStr,
70
+ logLabel
71
+ );
72
+ return getCodeHookResources(tenantId, categories, slugs);
73
+ }
74
+
75
+ export function buildCodeHookBladePath(
76
+ hookId: string,
77
+ ctx: Pick<CodeHookBladeContext, 'paneId' | 'tenantId' | 'optionsStr'>
78
+ ): string {
79
+ const params = new URLSearchParams({
80
+ paneId: ctx.paneId,
81
+ tenantId: ctx.tenantId,
82
+ });
83
+ if (ctx.optionsStr) {
84
+ params.set('options', ctx.optionsStr);
85
+ }
86
+ return `/codehooks/${hookId}?${params.toString()}`;
87
+ }
88
+
89
+ export function resolveSSRFetchOrigin(pageUrl: URL, siteUrl?: string): string {
90
+ if (siteUrl) {
91
+ try {
92
+ return new URL(siteUrl).origin;
93
+ } catch {
94
+ // fall through to page origin
95
+ }
96
+ }
97
+ return pageUrl.origin;
98
+ }
99
+
100
+ export async function fetchCodeHookBladeHtml(
101
+ bladePath: string,
102
+ request: Request,
103
+ origin: string
104
+ ): Promise<string> {
105
+ try {
106
+ const url = new URL(bladePath, origin);
107
+ const res = await fetch(url, {
108
+ headers: {
109
+ cookie: request.headers.get('cookie') ?? '',
110
+ accept: 'text/html',
111
+ },
112
+ });
113
+ if (!res.ok) {
114
+ console.error(
115
+ `Failed to SSR-fetch codehook blade ${bladePath}. Status: ${res.status}`
116
+ );
117
+ return '';
118
+ }
119
+ return res.text();
120
+ } catch (error) {
121
+ console.error(`Error SSR-fetching codehook blade ${bladePath}:`, error);
122
+ return '';
123
+ }
124
+ }
125
+
126
+ export async function fetchCodeHookBladesForPanes(options: {
127
+ paneIds: string[];
128
+ codeHookTargets: Record<string, string>;
129
+ tenantId: string;
130
+ request: Request;
131
+ origin: string;
132
+ }): Promise<Record<string, string>> {
133
+ const { paneIds, codeHookTargets, tenantId, request, origin } = options;
134
+ const result: Record<string, string> = {};
135
+
136
+ await Promise.all(
137
+ paneIds
138
+ .filter((paneId) => codeHookTargets[paneId])
139
+ .map(async (paneId) => {
140
+ const hookId = codeHookTargets[paneId];
141
+ const optionsStr = codeHookTargets[`${paneId}-${hookId}`] || '';
142
+ const bladePath = buildCodeHookBladePath(hookId, {
143
+ paneId,
144
+ tenantId,
145
+ optionsStr,
146
+ });
147
+ result[paneId] = await fetchCodeHookBladeHtml(
148
+ bladePath,
149
+ request,
150
+ origin
151
+ );
152
+ })
153
+ );
154
+
155
+ return result;
156
+ }
@@ -1,6 +1,47 @@
1
1
  import { headerResourcesStore, HEADER_RESOURCES_TTL } from '@/stores/resources';
2
2
  import type { ResourceNode } from '@/types/compositorTypes';
3
3
 
4
+ // Stateless resource fetch for codehook blades. POSTs categories/slugs to the
5
+ // nodes/resources endpoint, unwraps the { resources, count } envelope, and
6
+ // returns the FLAT ResourceNode[] (the API shape) unchanged - no grouping.
7
+ // Components that need a keyed view reshape internally. No cache.
8
+ export async function getCodeHookResources(
9
+ tenantId: string,
10
+ categories: string[] = [],
11
+ slugs: string[] = []
12
+ ): Promise<ResourceNode[]> {
13
+ if (categories.length === 0 && slugs.length === 0) {
14
+ return [];
15
+ }
16
+
17
+ const goBackend =
18
+ import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
19
+
20
+ try {
21
+ const response = await fetch(`${goBackend}/api/v1/nodes/resources`, {
22
+ method: 'POST',
23
+ headers: {
24
+ 'Content-Type': 'application/json',
25
+ 'X-Tenant-ID': tenantId,
26
+ },
27
+ body: JSON.stringify({ categories, slugs }),
28
+ });
29
+
30
+ if (!response.ok) {
31
+ console.error(
32
+ `Failed to fetch codehook resources. Status: ${response.status}`
33
+ );
34
+ return [];
35
+ }
36
+
37
+ const payload = await response.json();
38
+ return (payload.resources as ResourceNode[]) || [];
39
+ } catch (error) {
40
+ console.error('Error fetching codehook resources:', error);
41
+ return [];
42
+ }
43
+ }
44
+
4
45
  export async function getHeaderResources(
5
46
  tenantId: string,
6
47
  categories: string[],
@@ -1,5 +1,5 @@
1
1
  import { handleFailedResponse } from '@/utils/backend';
2
- import type { ImpressionNode, ResourceNode } from '@/types/compositorTypes';
2
+ import type { ImpressionNode } from '@/types/compositorTypes';
3
3
 
4
4
  export interface StoryData {
5
5
  id: string;
@@ -8,7 +8,6 @@ export interface StoryData {
8
8
  paneIds: string[];
9
9
  codeHookTargets: Record<string, string>;
10
10
  codeHookVisibility: Record<string, boolean | string[]>;
11
- resourcesPayload: Record<string, ResourceNode[]>;
12
11
  impressions: ImpressionNode[];
13
12
  fragments: Record<string, string>;
14
13
  menu: any;
@@ -6,7 +6,7 @@ import { getFullContentMap } from '@/stores/analytics';
6
6
  import { getBrandConfig } from '@/utils/api/brandConfig';
7
7
  import { joinUrlPaths } from '@/utils/helpers';
8
8
  import { handleFailedResponse } from '@/utils/backend';
9
- import { components as codeHookComponents } from '@/custom/CodeHook.astro';
9
+ import { availableCodeHookIds } from '@/custom/codehooks';
10
10
  import StoryKeepHeader from '@/components/edit/Header';
11
11
  import StoryKeepToolBar from '@/components/edit/ToolBar';
12
12
  import StoryKeepToolMode from '@/components/edit/ToolMode';
@@ -188,7 +188,7 @@ for (const [key, value] of Astro.url.searchParams) {
188
188
  fullContentMap={fullContentMap}
189
189
  fullCanonicalURL={fullCanonicalURL}
190
190
  urlParams={urlParams}
191
- availableCodeHooks={Object.keys(codeHookComponents)}
191
+ availableCodeHooks={availableCodeHookIds}
192
192
  client:only="react"
193
193
  />
194
194
  </div>
@@ -213,7 +213,7 @@ for (const [key, value] of Astro.url.searchParams) {
213
213
  }
214
214
  <div class="pointer-events-auto max-h-full">
215
215
  <SettingsPanel
216
- availableCodeHooks={Object.keys(codeHookComponents)}
216
+ availableCodeHooks={availableCodeHookIds}
217
217
  client:only="react"
218
218
  />
219
219
  </div>
@@ -1,10 +1,13 @@
1
1
  ---
2
2
  import Layout from '@/layouts/Layout.astro';
3
- import CodeHook from '@/custom/CodeHook.astro';
4
3
  import { getBrandConfig } from '@/utils/api/brandConfig';
5
4
  import { getFullContentMap } from '@/stores/analytics';
6
5
  import { getOrSetSessionId } from '@/lib/session';
7
6
  import { getStoryData } from '@/lib/storyData';
7
+ import {
8
+ fetchCodeHookBladesForPanes,
9
+ resolveSSRFetchOrigin,
10
+ } from '@/lib/codeHookHelper';
8
11
  import { preHealthCheck } from '@/utils/backend';
9
12
 
10
13
  const tenantId =
@@ -43,7 +46,6 @@ const storyfragmentId = storyData.id;
43
46
  const storyfragmentTitle = storyData.title || 'Untitled Story';
44
47
  const paneIds = storyData.paneIds || [];
45
48
  const codeHookTargets = storyData.codeHookTargets || {};
46
- const resourcesPayload = storyData.resourcesPayload || {};
47
49
 
48
50
  if (paneIds.length === 0) {
49
51
  console.log(`Empty Story Fragment. Redirecting to /storykeep`);
@@ -66,6 +68,14 @@ if (!brandConfig.SITE_INIT) {
66
68
  return Astro.redirect('/storykeep');
67
69
  }
68
70
 
71
+ const codeHookHtml = await fetchCodeHookBladesForPanes({
72
+ paneIds,
73
+ codeHookTargets,
74
+ tenantId,
75
+ request: Astro.request,
76
+ origin: resolveSSRFetchOrigin(Astro.url, brandConfig.SITE_URL),
77
+ });
78
+
69
79
  const ogImage =
70
80
  typeof storyData.socialImagePath === `string`
71
81
  ? storyData.socialImagePath
@@ -95,77 +105,73 @@ paneIds.forEach((paneId: string) => {
95
105
  <main id="main-content" class="w-full">
96
106
  <div class="panes-container">
97
107
  {
98
- paneIds.map((paneId: string) => (
99
- <div id={paneSlugLookup[paneId]}>
100
- <div
101
- id={`pane-${paneId}`}
102
- data-pane-id={paneId}
103
- class="pane-fragment-container"
104
- style={
105
- !codeHookTargets[paneId]
106
- ? undefined
107
- : !storyData.codeHookVisibility?.[paneId]
108
- ? 'display:none;'
109
- : 'display:block;'
110
- }
111
- hx-get={`/api/v1/fragments/panes/${paneId}`}
112
- hx-trigger="refresh"
113
- hx-swap="innerHTML scroll:none"
114
- >
115
- {codeHookTargets[paneId] ? (
116
- <div class="relative overflow-hidden">
117
- <CodeHook
118
- target={codeHookTargets[paneId]}
119
- paneId={paneId}
120
- options={(() => {
121
- const optionsStr =
122
- codeHookTargets[paneId + '-' + codeHookTargets[paneId]];
123
- return optionsStr
124
- ? { params: { options: optionsStr } }
125
- : undefined;
126
- })()}
127
- fullContentMap={fullContentMap}
128
- resourcesPayload={resourcesPayload}
129
- />
130
- <div id={`pane-${paneId}-unset`}>
131
- {Array.isArray(storyData.codeHookVisibility?.[paneId]) && (
132
- <button
133
- type="button"
134
- class="absolute right-2 top-2 z-10 rounded-full bg-white p-1.5 text-mydarkgrey hover:bg-black hover:text-white"
135
- title="Go Back"
136
- hx-post="/api/v1/state"
137
- hx-trigger="click"
138
- hx-swap="none"
139
- hx-vals={JSON.stringify({
140
- unsetBeliefIds:
141
- storyData.codeHookVisibility[paneId].join(','),
142
- paneId: paneId,
143
- })}
144
- hx-preserve="true"
145
- >
146
- <svg
147
- class="h-6 w-6"
148
- fill="none"
149
- viewBox="0 0 24 24"
150
- stroke-width="1.5"
151
- stroke="currentColor"
108
+ paneIds.map((paneId: string) => {
109
+ const hookId = codeHookTargets[paneId];
110
+ const isCodeHook = !!hookId;
111
+ return (
112
+ <div id={paneSlugLookup[paneId]}>
113
+ <div
114
+ id={`pane-${paneId}`}
115
+ data-pane-id={paneId}
116
+ class="pane-fragment-container"
117
+ style={
118
+ !isCodeHook
119
+ ? undefined
120
+ : !storyData.codeHookVisibility?.[paneId]
121
+ ? 'display:none;'
122
+ : 'display:block;'
123
+ }
124
+ hx-get={
125
+ !isCodeHook ? `/api/v1/fragments/panes/${paneId}` : undefined
126
+ }
127
+ hx-trigger={!isCodeHook ? 'refresh' : undefined}
128
+ hx-swap={!isCodeHook ? 'innerHTML scroll:none' : undefined}
129
+ >
130
+ {isCodeHook ? (
131
+ <div class="relative overflow-hidden">
132
+ <Fragment set:html={codeHookHtml[paneId] || ''} />
133
+ <div id={`pane-${paneId}-unset`}>
134
+ {Array.isArray(
135
+ storyData.codeHookVisibility?.[paneId]
136
+ ) && (
137
+ <button
138
+ type="button"
139
+ class="absolute right-2 top-2 z-10 rounded-full bg-white p-1.5 text-mydarkgrey hover:bg-black hover:text-white"
140
+ title="Go Back"
141
+ hx-post="/api/v1/state"
142
+ hx-trigger="click"
143
+ hx-swap="none"
144
+ hx-vals={JSON.stringify({
145
+ unsetBeliefIds:
146
+ storyData.codeHookVisibility[paneId].join(','),
147
+ paneId: paneId,
148
+ })}
149
+ hx-preserve="true"
152
150
  >
153
- <path
154
- stroke-linecap="round"
155
- stroke-linejoin="round"
156
- d="M9 15L3 9m0 0l6-6M3 9h12a6 6 0 010 12h-3"
157
- />
158
- </svg>
159
- </button>
160
- )}
151
+ <svg
152
+ class="h-6 w-6"
153
+ fill="none"
154
+ viewBox="0 0 24 24"
155
+ stroke-width="1.5"
156
+ stroke="currentColor"
157
+ >
158
+ <path
159
+ stroke-linecap="round"
160
+ stroke-linejoin="round"
161
+ d="M9 15L3 9m0 0l6-6M3 9h12a6 6 0 010 12h-3"
162
+ />
163
+ </svg>
164
+ </button>
165
+ )}
166
+ </div>
161
167
  </div>
162
- </div>
163
- ) : (
164
- <Fragment set:html={storyData.fragments[paneId] || ''} />
165
- )}
168
+ ) : (
169
+ <Fragment set:html={storyData.fragments[paneId] || ''} />
170
+ )}
171
+ </div>
166
172
  </div>
167
- </div>
168
- ))
173
+ );
174
+ })
169
175
  }
170
176
  </div>
171
177
  </main>
@@ -0,0 +1,18 @@
1
+ ---
2
+ export const partial = true;
3
+ const hookId = Astro.params.hookId || '';
4
+ ---
5
+
6
+ <div class="my-4 w-full bg-gray-50 p-6">
7
+ <div
8
+ class="mb-4 rounded-lg border-2 border-dashed border-gray-300 bg-slate-50 p-6"
9
+ >
10
+ <h3 class="text-lg text-gray-700">
11
+ Code Hook:{' '}
12
+ <span class="font-action font-bold">{hookId || 'unknown'}</span>
13
+ </h3>
14
+ <p class="mt-2 text-sm text-gray-500">
15
+ CodeHook not activated for this install.
16
+ </p>
17
+ </div>
18
+ </div>
@@ -0,0 +1,9 @@
1
+ ---
2
+ export const partial = true;
3
+ import BunnyVideoWrapper from '@/components/codehooks/BunnyVideoWrapper.astro';
4
+ import { parseCodeHookBladeContext } from '@/lib/codeHookHelper';
5
+
6
+ const { options } = parseCodeHookBladeContext(Astro.url.searchParams);
7
+ ---
8
+
9
+ <BunnyVideoWrapper options={options} />
@@ -0,0 +1,6 @@
1
+ ---
2
+ export const partial = true;
3
+ import CustomHero from '@/custom/CustomHero.astro';
4
+ ---
5
+
6
+ <CustomHero />
@@ -0,0 +1,15 @@
1
+ ---
2
+ export const partial = true;
3
+ import EpinetWrapper from '@/components/codehooks/EpinetWrapper';
4
+ import {
5
+ parseCodeHookBladeContext,
6
+ resolveCodeHookFullContentMap,
7
+ } from '@/lib/codeHookHelper';
8
+
9
+ const { tenantId } = parseCodeHookBladeContext(Astro.url.searchParams);
10
+ const fullContentMap = await resolveCodeHookFullContentMap(tenantId);
11
+ ---
12
+
13
+ <div style="min-height: 400px;">
14
+ <EpinetWrapper fullContentMap={fullContentMap} client:only="react" />
15
+ </div>
@@ -0,0 +1,13 @@
1
+ ---
2
+ export const partial = true;
3
+ import FeaturedArticle from '@/components/codehooks/FeaturedArticle.astro';
4
+ import {
5
+ parseCodeHookBladeContext,
6
+ resolveCodeHookFullContentMap,
7
+ } from '@/lib/codeHookHelper';
8
+
9
+ const { tenantId, options } = parseCodeHookBladeContext(Astro.url.searchParams);
10
+ const fullContentMap = await resolveCodeHookFullContentMap(tenantId);
11
+ ---
12
+
13
+ <FeaturedArticle options={options} fullContentMap={fullContentMap} />
@@ -0,0 +1,8 @@
1
+ ---
2
+ export const partial = true;
3
+ import SandboxLauncher from '@/custom/SandboxLauncher';
4
+ ---
5
+
6
+ <div style="min-height: 400px;">
7
+ <SandboxLauncher client:only="react" />
8
+ </div>
@@ -0,0 +1,13 @@
1
+ ---
2
+ export const partial = true;
3
+ import ListContent from '@/components/codehooks/ListContent.astro';
4
+ import {
5
+ parseCodeHookBladeContext,
6
+ resolveCodeHookFullContentMap,
7
+ } from '@/lib/codeHookHelper';
8
+
9
+ const { tenantId, options } = parseCodeHookBladeContext(Astro.url.searchParams);
10
+ const fullContentMap = await resolveCodeHookFullContentMap(tenantId);
11
+ ---
12
+
13
+ <ListContent options={options} fullContentMap={fullContentMap} />
@@ -0,0 +1,13 @@
1
+ ---
2
+ export const partial = true;
3
+ import SearchWidget from '@/components/codehooks/SearchWidget.tsx';
4
+ import {
5
+ parseCodeHookBladeContext,
6
+ resolveCodeHookFullContentMap,
7
+ } from '@/lib/codeHookHelper';
8
+
9
+ const { tenantId } = parseCodeHookBladeContext(Astro.url.searchParams);
10
+ const fullContentMap = await resolveCodeHookFullContentMap(tenantId);
11
+ ---
12
+
13
+ <SearchWidget fullContentMap={fullContentMap} client:load />
@@ -0,0 +1,23 @@
1
+ ---
2
+ export const partial = true;
3
+ import ShopifyProductGrid from '@/custom/shopify/ShopifyProductGrid';
4
+ import {
5
+ parseCodeHookBladeContext,
6
+ resolveCodeHookResources,
7
+ } from '@/lib/codeHookHelper';
8
+
9
+ const { tenantId, optionsStr, options } = parseCodeHookBladeContext(
10
+ Astro.url.searchParams
11
+ );
12
+ const resources = await resolveCodeHookResources(
13
+ tenantId,
14
+ optionsStr,
15
+ 'shopify-product-grid'
16
+ );
17
+ ---
18
+
19
+ <ShopifyProductGrid
20
+ options={options}
21
+ resources={resources}
22
+ client:only="react"
23
+ />
@@ -0,0 +1,23 @@
1
+ ---
2
+ export const partial = true;
3
+ import ShopifyServiceList from '@/custom/shopify/ShopifyServiceList';
4
+ import {
5
+ parseCodeHookBladeContext,
6
+ resolveCodeHookResources,
7
+ } from '@/lib/codeHookHelper';
8
+
9
+ const { tenantId, optionsStr, options } = parseCodeHookBladeContext(
10
+ Astro.url.searchParams
11
+ );
12
+ const resources = await resolveCodeHookResources(
13
+ tenantId,
14
+ optionsStr,
15
+ 'shopify-service-list'
16
+ );
17
+ ---
18
+
19
+ <ShopifyServiceList
20
+ options={options}
21
+ resources={resources}
22
+ client:only="react"
23
+ />
@@ -6,7 +6,7 @@ import { getFullContentMap } from '@/stores/analytics';
6
6
  import { getBrandConfig } from '@/utils/api/brandConfig';
7
7
  import { joinUrlPaths } from '@/utils/helpers';
8
8
  import { handleFailedResponse } from '@/utils/backend';
9
- import { components as codeHookComponents } from '@/custom/CodeHook.astro';
9
+ import { availableCodeHookIds } from '@/custom/codehooks';
10
10
  import StoryKeepHeader from '@/components/edit/Header';
11
11
  import StoryKeepToolBar from '@/components/edit/ToolBar';
12
12
  import StoryKeepToolMode from '@/components/edit/ToolMode';
@@ -175,7 +175,7 @@ for (const [key, value] of Astro.url.searchParams) {
175
175
  fullContentMap={fullContentMap}
176
176
  fullCanonicalURL={canonicalURL}
177
177
  urlParams={urlParams}
178
- availableCodeHooks={Object.keys(codeHookComponents)}
178
+ availableCodeHooks={availableCodeHookIds}
179
179
  client:only="react"
180
180
  />
181
181
  </div>
@@ -201,7 +201,7 @@ for (const [key, value] of Astro.url.searchParams) {
201
201
  <div class="pointer-events-auto max-h-full">
202
202
  <SettingsPanel
203
203
  config={brandConfig}
204
- availableCodeHooks={Object.keys(codeHookComponents)}
204
+ availableCodeHooks={availableCodeHookIds}
205
205
  client:only="react"
206
206
  />
207
207
  </div>