astro-tractstack 2.2.10 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +1 -1
  2. package/bin/create-tractstack.js +2 -2
  3. package/dist/index.js +177 -18
  4. package/package.json +4 -2
  5. package/templates/custom/minimal/CodeHook.astro +22 -5
  6. package/templates/custom/shopify/Cart.tsx +372 -0
  7. package/templates/custom/shopify/CartIcon.tsx +47 -0
  8. package/templates/custom/shopify/CartModal.tsx +63 -0
  9. package/templates/custom/shopify/CheckoutModal.tsx +576 -0
  10. package/templates/custom/shopify/NativeBookingCalendar.tsx +375 -0
  11. package/templates/custom/shopify/ShopifyCartManager.tsx +200 -0
  12. package/templates/custom/shopify/ShopifyCheckout.tsx +167 -0
  13. package/templates/custom/shopify/ShopifyProductGrid.tsx +247 -0
  14. package/templates/custom/shopify/ShopifyServiceList.tsx +135 -0
  15. package/templates/custom/shopify/cart.astro +23 -0
  16. package/templates/custom/with-examples/CodeHook.astro +17 -1
  17. package/templates/custom/with-examples/ProductGrid.astro +1 -1
  18. package/templates/src/client/app.js +4 -2
  19. package/templates/src/components/Footer.astro +4 -4
  20. package/templates/src/components/Header.astro +44 -12
  21. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +3 -3
  22. package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +2 -2
  23. package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +2 -2
  24. package/templates/src/components/edit/pane/steps/AiLibraryCopyStep.tsx +3 -3
  25. package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +2 -2
  26. package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +7 -7
  27. package/templates/src/components/form/advanced/APIConfigSection.tsx +407 -38
  28. package/templates/src/components/form/shopify/SchedulingSection.tsx +354 -0
  29. package/templates/src/components/storykeep/Dashboard.tsx +18 -4
  30. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +1 -0
  31. package/templates/src/components/storykeep/Dashboard_Content.tsx +5 -96
  32. package/templates/src/components/storykeep/Dashboard_Shopify.tsx +668 -0
  33. package/templates/src/components/storykeep/StoryKeepBackdrop.astro +43 -23
  34. package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +14 -5
  35. package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +0 -14
  36. package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +36 -13
  37. package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +5 -2
  38. package/templates/src/components/storykeep/controls/content/ManageContent.tsx +4 -11
  39. package/templates/src/components/storykeep/controls/content/MenuTable.tsx +14 -5
  40. package/templates/src/components/storykeep/controls/content/ProductTable.tsx +333 -0
  41. package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +9 -5
  42. package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +108 -8
  43. package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +13 -4
  44. package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +14 -5
  45. package/templates/src/components/storykeep/shopify/ShopifyDashboard.tsx +111 -0
  46. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx +393 -0
  47. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Products.tsx +46 -0
  48. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Schedule.tsx +78 -0
  49. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Search.tsx +55 -0
  50. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Services.tsx +47 -0
  51. package/templates/src/lib/resources.ts +11 -21
  52. package/templates/src/pages/api/auth/lookup-lead.ts +72 -0
  53. package/templates/src/pages/api/booking/availability.ts +72 -0
  54. package/templates/src/pages/api/booking/cancel.ts +73 -0
  55. package/templates/src/pages/api/booking/confirm.ts +82 -0
  56. package/templates/src/pages/api/booking/hold.ts +75 -0
  57. package/templates/src/pages/api/booking/list.ts +66 -0
  58. package/templates/src/pages/api/booking/metrics.ts +60 -0
  59. package/templates/src/pages/api/booking/release.ts +76 -0
  60. package/templates/src/pages/api/sandbox.ts +2 -2
  61. package/templates/src/pages/api/shopify/createCart.ts +69 -0
  62. package/templates/src/pages/api/shopify/getProducts.ts +64 -0
  63. package/templates/src/pages/storykeep/login.astro +26 -24
  64. package/templates/src/pages/storykeep/logout.astro +1 -10
  65. package/templates/src/pages/storykeep/manage.astro +69 -0
  66. package/templates/src/pages/storykeep/{content.astro → pages.astro} +4 -8
  67. package/templates/src/pages/storykeep/shopify.astro +101 -0
  68. package/templates/src/stores/navigation.ts +3 -42
  69. package/templates/src/stores/nodes.ts +3 -1
  70. package/templates/src/stores/resources.ts +7 -10
  71. package/templates/src/stores/shopify.ts +266 -0
  72. package/templates/src/types/tractstack.ts +75 -0
  73. package/templates/src/utils/api/advancedConfig.ts +7 -1
  74. package/templates/src/utils/api/advancedHelpers.ts +87 -7
  75. package/templates/src/utils/api/bookingHelpers.ts +125 -0
  76. package/templates/src/utils/api/brandHelpers.ts +14 -0
  77. package/templates/src/utils/api/resourceConfig.ts +13 -5
  78. package/templates/src/utils/auth.ts +29 -9
  79. package/templates/src/utils/compositor/aiGeneration.ts +3 -3
  80. package/templates/src/utils/compositor/aiPaneParser.ts +2 -2
  81. package/templates/src/utils/customHelpers.ts +49 -0
  82. package/templates/src/utils/helpers.ts +59 -0
  83. package/templates/src/utils/profileStorage.ts +5 -0
  84. package/templates/src/utils/tenantResolver.ts +2 -1
  85. package/utils/inject-files.ts +161 -2
@@ -104,11 +104,13 @@ export async function deleteResource(
104
104
 
105
105
  export async function getAllResourceIds(tenantId: string): Promise<string[]> {
106
106
  const api = new TractStackAPI(tenantId);
107
- const response = await api.get('/api/v1/nodes/resources');
107
+ const response = await api.get<{ count: number; resourceIds: string[] }>(
108
+ '/api/v1/nodes/resources'
109
+ );
108
110
  if (!response.success) {
109
111
  throw new Error(response.error || 'Failed to get resource IDs');
110
112
  }
111
- return response.data;
113
+ return response.data?.resourceIds || [];
112
114
  }
113
115
 
114
116
  export async function getResourcesByIds(
@@ -116,11 +118,14 @@ export async function getResourcesByIds(
116
118
  ids: string[]
117
119
  ): Promise<ResourceConfig[]> {
118
120
  const api = new TractStackAPI(tenantId);
119
- const response = await api.post('/api/v1/nodes/resources', { ids });
120
- if (!response.success) {
121
+ const response = await api.post<{ resources: ResourceConfig[] }>(
122
+ '/api/v1/nodes/resources',
123
+ { resourceIds: ids }
124
+ );
125
+ if (!response.success || !response.data) {
121
126
  throw new Error(response.error || 'Failed to get resources by IDs');
122
127
  }
123
- return response.data;
128
+ return response.data.resources;
124
129
  }
125
130
 
126
131
  export async function getResourcesByCategory(
@@ -128,6 +133,9 @@ export async function getResourcesByCategory(
128
133
  categorySlug: string
129
134
  ): Promise<ResourceConfig[]> {
130
135
  const allIds = await getAllResourceIds(tenantId);
136
+ if (!allIds || allIds.length === 0) {
137
+ return [];
138
+ }
131
139
  const allResources = await getResourcesByIds(tenantId, allIds);
132
140
  return allResources.filter(
133
141
  (resource) => resource.categorySlug === categorySlug
@@ -11,20 +11,30 @@ export interface AdminAuthClaims {
11
11
  exp: number;
12
12
  }
13
13
 
14
+ /**
15
+ * Helper to get the current expected tenant ID from the Astro context
16
+ */
17
+ function getExpectedTenantId(astro: any): string {
18
+ return (
19
+ astro.locals?.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default'
20
+ );
21
+ }
22
+
14
23
  /**
15
24
  * Check if user is authenticated (either admin or editor)
16
25
  */
17
26
  export function isAuthenticated(astro: any): boolean {
27
+ const expectedTenantId = getExpectedTenantId(astro);
18
28
  const adminCookie = astro.cookies.get('admin_auth');
19
29
  const editorCookie = astro.cookies.get('editor_auth');
20
30
 
21
31
  if (adminCookie?.value) {
22
- const claims = validateAdminToken(adminCookie.value);
32
+ const claims = validateAdminToken(adminCookie.value, expectedTenantId);
23
33
  if (claims && claims.role === 'admin') return true;
24
34
  }
25
35
 
26
36
  if (editorCookie?.value) {
27
- const claims = validateAdminToken(editorCookie.value);
37
+ const claims = validateAdminToken(editorCookie.value, expectedTenantId);
28
38
  if (claims && claims.role === 'editor') return true;
29
39
  }
30
40
 
@@ -35,10 +45,11 @@ export function isAuthenticated(astro: any): boolean {
35
45
  * Check if user has admin role
36
46
  */
37
47
  export function isAdmin(astro: any): boolean {
48
+ const expectedTenantId = getExpectedTenantId(astro);
38
49
  const adminCookie = astro.cookies.get('admin_auth');
39
50
  if (!adminCookie?.value) return false;
40
51
 
41
- const claims = validateAdminToken(adminCookie.value);
52
+ const claims = validateAdminToken(adminCookie.value, expectedTenantId);
42
53
  return claims?.role === 'admin';
43
54
  }
44
55
 
@@ -46,10 +57,11 @@ export function isAdmin(astro: any): boolean {
46
57
  * Check if user has editor role
47
58
  */
48
59
  export function isEditor(astro: any): boolean {
60
+ const expectedTenantId = getExpectedTenantId(astro);
49
61
  const editorCookie = astro.cookies.get('editor_auth');
50
62
  if (!editorCookie?.value) return false;
51
63
 
52
- const claims = validateAdminToken(editorCookie.value);
64
+ const claims = validateAdminToken(editorCookie.value, expectedTenantId);
53
65
  return claims?.role === 'editor';
54
66
  }
55
67
 
@@ -57,15 +69,16 @@ export function isEditor(astro: any): boolean {
57
69
  * Get user role (admin, editor, or null)
58
70
  */
59
71
  export function getUserRole(astro: any): 'admin' | 'editor' | null {
72
+ const expectedTenantId = getExpectedTenantId(astro);
60
73
  const adminCookie = astro.cookies.get('admin_auth');
61
74
  if (adminCookie?.value) {
62
- const claims = validateAdminToken(adminCookie.value);
75
+ const claims = validateAdminToken(adminCookie.value, expectedTenantId);
63
76
  if (claims?.role === 'admin') return 'admin';
64
77
  }
65
78
 
66
79
  const editorCookie = astro.cookies.get('editor_auth');
67
80
  if (editorCookie?.value) {
68
- const claims = validateAdminToken(editorCookie.value);
81
+ const claims = validateAdminToken(editorCookie.value, expectedTenantId);
69
82
  if (claims?.role === 'editor') return 'editor';
70
83
  }
71
84
 
@@ -110,15 +123,16 @@ export function requireAdminOrEditor(astro: any): Response | undefined {
110
123
  * Returns the JWT token from the appropriate cookie
111
124
  */
112
125
  export function getAdminToken(astro: any): string | null {
126
+ const expectedTenantId = getExpectedTenantId(astro);
113
127
  const adminCookie = astro.cookies.get('admin_auth');
114
128
  if (adminCookie?.value) {
115
- const claims = validateAdminToken(adminCookie.value);
129
+ const claims = validateAdminToken(adminCookie.value, expectedTenantId);
116
130
  if (claims?.role === 'admin') return adminCookie.value;
117
131
  }
118
132
 
119
133
  const editorCookie = astro.cookies.get('editor_auth');
120
134
  if (editorCookie?.value) {
121
- const claims = validateAdminToken(editorCookie.value);
135
+ const claims = validateAdminToken(editorCookie.value, expectedTenantId);
122
136
  if (claims?.role === 'editor') return editorCookie.value;
123
137
  }
124
138
 
@@ -130,7 +144,10 @@ export function getAdminToken(astro: any): string | null {
130
144
  * Note: This is a simplified client-side validation
131
145
  * Real validation happens on the backend
132
146
  */
133
- function validateAdminToken(token: string): AdminAuthClaims | null {
147
+ function validateAdminToken(
148
+ token: string,
149
+ expectedTenantId: string
150
+ ): AdminAuthClaims | null {
134
151
  try {
135
152
  // Split JWT token
136
153
  const parts = token.split('.');
@@ -146,6 +163,9 @@ function validateAdminToken(token: string): AdminAuthClaims | null {
146
163
  if (!claims.role || !['admin', 'editor'].includes(claims.role)) return null;
147
164
  if (Date.now() / 1000 > claims.exp) return null; // Token expired
148
165
 
166
+ // Tenant validation
167
+ if (claims.tenantId !== expectedTenantId) return null; // Must match current environment
168
+
149
169
  return claims;
150
170
  } catch {
151
171
  return null;
@@ -10,7 +10,7 @@ interface AiGenerationOptions {
10
10
  temperature?: number;
11
11
  }
12
12
 
13
- export const callAskLemurAPI = async ({
13
+ export const callAaiAPI = async ({
14
14
  prompt,
15
15
  context,
16
16
  expectJson,
@@ -43,7 +43,7 @@ export const callAskLemurAPI = async ({
43
43
  'X-Sandbox-Token': token || '',
44
44
  },
45
45
  credentials: 'include',
46
- body: JSON.stringify({ action: 'askLemur', payload: requestBody }),
46
+ body: JSON.stringify({ action: 'aai', payload: requestBody }),
47
47
  });
48
48
 
49
49
  if (!response.ok) {
@@ -58,7 +58,7 @@ export const callAskLemurAPI = async ({
58
58
  resultData = json.data;
59
59
  } else {
60
60
  const api = new TractStackAPI(tenantId);
61
- const response = await api.post('/api/v1/aai/askLemur', requestBody);
61
+ const response = await api.post('/api/v1/aai/aai', requestBody);
62
62
 
63
63
  if (!response.success) {
64
64
  throw new Error(
@@ -771,11 +771,11 @@ export function createDefaultShell(layout: 'standard' | 'grid'): ShellJson {
771
771
  mobile: 'mb-2',
772
772
  },
773
773
  a: {
774
- mobile: 'text-indigo-600 hover:text-indigo-500 font-semibold',
774
+ mobile: 'text-cyan-600 hover:text-cyan-500 font-bold',
775
775
  },
776
776
  button: {
777
777
  mobile:
778
- 'rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600',
778
+ 'rounded-md bg-cyan-600 px-3.5 py-2.5 text-sm font-bold text-white shadow-sm hover:bg-cyan-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cyan-600',
779
779
  },
780
780
  };
781
781
 
@@ -1,3 +1,6 @@
1
+ import type { ResourceNode } from '@/types/compositorTypes';
2
+ import type { CartItemState } from '@/stores/shopify';
3
+
1
4
  // URL Helper: Strip category prefix from slug
2
5
  // e.g., "people-bleako" -> "bleako"
3
6
  export function getCleanSlug(categorySlug: string, fullSlug: string): string {
@@ -36,3 +39,49 @@ export function initSearch(): void {
36
39
  // Default implementation does nothing
37
40
  // Override this function in your custom implementation to load search data
38
41
  }
42
+
43
+ // Field Visibility Controls for ResourceForm
44
+ export const resourceFormHideFields = ['gid', 'shopifyImage'];
45
+
46
+ // Field Formatting Controls for ResourceForm
47
+ // Fields listed here will be treated as JSON objects but rendered as stringified text areas
48
+ export const resourceJsonifyFields = ['shopifyData', 'shopifyImage'];
49
+
50
+ export const RESTRICTION_MESSAGES = {
51
+ BOOKING: (duration: number) =>
52
+ `This is a ${duration} minute service. On checkout we'll help you book at your convenience.`,
53
+ TERMS: 'Please review the terms for this item before adding it to your cart.',
54
+ MAX_DURATION: (max: number) =>
55
+ `You cannot book more than ${max} minutes of services in one session.`,
56
+ DEFAULT_ADD: (title: string) => `${title} has been added to your cart.`,
57
+ };
58
+
59
+ // For CartModal.tsx
60
+ export function checkRestrictions(resource: ResourceNode): boolean {
61
+ // 1. Service / Booking Requirement
62
+ // We check for the explicit option payload value used by services
63
+ if (resource.optionsPayload?.bookingLengthMinutes) {
64
+ return true;
65
+ }
66
+
67
+ // 2. Final Sale / Terms Check
68
+ // Placeholder: In the future, check for flags like resource.optionsPayload?.finalSale
69
+ // if (resource.optionsPayload?.finalSale) {
70
+ // return true;
71
+ // }
72
+
73
+ return false;
74
+ }
75
+
76
+ export function calculateCartDuration(
77
+ cart: Record<string, CartItemState>,
78
+ resources: ResourceNode[]
79
+ ): number {
80
+ return Object.values(cart).reduce((total, item) => {
81
+ const resource = resources.find((r) => r.id === item.resourceId);
82
+ const duration = Number(
83
+ resource?.optionsPayload?.bookingLengthMinutes || 0
84
+ );
85
+ return total + (isNaN(duration) ? 0 : duration * item.quantity);
86
+ }, 0);
87
+ }
@@ -2,6 +2,7 @@ import { useState, useEffect, useCallback } from 'react';
2
2
  import { stopWords } from '@/constants/stopWords';
3
3
  import type { RefObject } from 'react';
4
4
  import type { MenuNode } from '@/types/tractstack';
5
+ import type { ResourceNode } from '@/types/compositorTypes';
5
6
 
6
7
  let progressInterval: NodeJS.Timeout | null = null;
7
8
  let safetyTimeout: NodeJS.Timeout | null = null;
@@ -536,3 +537,61 @@ export const resolveCollisions = () => {
536
537
  }
537
538
  });
538
539
  };
540
+
541
+ // Shopify Image Helper: Returns responsive WebP paths for the resource image
542
+ export function getShopifyImage(
543
+ resource: ResourceNode,
544
+ size: '600' | '1080' | '1920' = '600',
545
+ variantId?: string
546
+ ): { src: string; srcSet: string } {
547
+ let imageId = resource.optionsPayload?.image;
548
+
549
+ if (variantId && typeof resource.optionsPayload?.shopifyImage === 'string') {
550
+ try {
551
+ const variantMap = JSON.parse(resource.optionsPayload.shopifyImage);
552
+ if (variantMap[variantId]?.fileId) {
553
+ imageId = variantMap[variantId].fileId;
554
+ }
555
+ } catch (e) {
556
+ console.warn(
557
+ `[Shopify] Failed to parse shopifyImage map for resource ${resource.id}`,
558
+ e
559
+ );
560
+ }
561
+ }
562
+
563
+ if (imageId && typeof imageId === 'string') {
564
+ const baseUrl = `/media/images/resources/${imageId}`;
565
+ return {
566
+ src: `${baseUrl}_${size}px.webp`,
567
+ srcSet: `${baseUrl}_1920px.webp 1920w, ${baseUrl}_1080px.webp 1080w, ${baseUrl}_600px.webp 600w`,
568
+ };
569
+ }
570
+
571
+ return {
572
+ src: '/static.jpg',
573
+ srcSet: '',
574
+ };
575
+ }
576
+
577
+ // Image Helper: Returns responsive WebP paths for the resource image
578
+ export function getResourceImage(
579
+ resource: ResourceNode,
580
+ size: '600' | '1080' | '1920' = '600'
581
+ ): { src: string; srcSet: string } {
582
+ const imageId = resource.optionsPayload?.image;
583
+
584
+ if (imageId && typeof imageId === 'string') {
585
+ const baseUrl = `/media/images/resources/${imageId}`;
586
+ return {
587
+ src: `${baseUrl}_${size}px.webp`,
588
+ srcSet: `${baseUrl}_1920px.webp 1920w, ${baseUrl}_1080px.webp 1080w, ${baseUrl}_600px.webp 600w`,
589
+ };
590
+ }
591
+
592
+ // Fallback for resources with no synced image
593
+ return {
594
+ src: '/static.jpg',
595
+ srcSet: '',
596
+ };
597
+ }
@@ -1,3 +1,5 @@
1
+ import { clearCommerceState } from '@/stores/shopify';
2
+
1
3
  export interface ProfileData {
2
4
  firstname?: string;
3
5
  contactPersona?: string;
@@ -148,6 +150,7 @@ export class ProfileStorage {
148
150
  StorageManager.set(this.STORAGE_KEYS.profileToken, token);
149
151
  StorageManager.set(this.STORAGE_KEYS.hasProfile, '1');
150
152
  StorageManager.set(this.STORAGE_KEYS.unlockedProfile, '1');
153
+ StorageManager.remove('shopify_customer');
151
154
 
152
155
  try {
153
156
  const maxAge = 60 * 60 * 24;
@@ -205,6 +208,7 @@ export class ProfileStorage {
205
208
  }
206
209
 
207
210
  console.log('TractStack: Session cleared completely including session ID');
211
+ clearCommerceState();
208
212
  }
209
213
 
210
214
  /**
@@ -218,6 +222,7 @@ export class ProfileStorage {
218
222
  StorageManager.remove(this.STORAGE_KEYS.contactPersona);
219
223
  StorageManager.remove(this.STORAGE_KEYS.email);
220
224
  StorageManager.remove(this.STORAGE_KEYS.shortBio);
225
+ clearCommerceState();
221
226
  }
222
227
 
223
228
  /**
@@ -65,13 +65,14 @@ export async function resolveTenantId(
65
65
  // Strategy 3: Backend Lookup (Fallback - Network Request)
66
66
  try {
67
67
  const backendUrl =
68
- import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:10000';
68
+ import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
69
69
  const urlObj = new URL(backendUrl);
70
70
  // Force localhost to avoid Hairpin NAT / Loopback firewall blocks
71
71
  const localBackend = `${urlObj.protocol}//127.0.0.1:${urlObj.port}`;
72
72
 
73
73
  if (VERBOSE) console.log(`[TenantResolver] Fetching from: ${localBackend}`);
74
74
 
75
+ console.log(`[TenantResolver] Activating local bypass`);
75
76
  // Temporarily disable TLS validation because 127.0.0.1 won't match the cert
76
77
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
77
78
 
@@ -588,6 +588,10 @@ export async function injectTemplateFiles(
588
588
  src: resolve('../templates/src/stores/resources.ts'),
589
589
  dest: 'src/stores/resources.ts',
590
590
  },
591
+ {
592
+ src: resolve('../templates/src/stores/shopify.ts'),
593
+ dest: 'src/stores/shopify.ts',
594
+ },
591
595
 
592
596
  // Compositor stores
593
597
  {
@@ -818,6 +822,10 @@ export async function injectTemplateFiles(
818
822
  src: resolve('../templates/src/utils/api/resourceHelpers.ts'),
819
823
  dest: 'src/utils/api/resourceHelpers.ts',
820
824
  },
825
+ {
826
+ src: resolve('../templates/src/utils/api/bookingHelpers.ts'),
827
+ dest: 'src/utils/api/bookingHelpers.ts',
828
+ },
821
829
  {
822
830
  src: resolve('../templates/src/utils/api/menuHelpers.ts'),
823
831
  dest: 'src/utils/api/menuHelpers.ts',
@@ -865,8 +873,12 @@ export async function injectTemplateFiles(
865
873
  dest: 'src/pages/storykeep.astro',
866
874
  },
867
875
  {
868
- src: resolve('../templates/src/pages/storykeep/content.astro'),
869
- dest: 'src/pages/storykeep/content.astro',
876
+ src: resolve('../templates/src/pages/storykeep/pages.astro'),
877
+ dest: 'src/pages/storykeep/pages.astro',
878
+ },
879
+ {
880
+ src: resolve('../templates/src/pages/storykeep/manage.astro'),
881
+ dest: 'src/pages/storykeep/manage.astro',
870
882
  },
871
883
  {
872
884
  src: resolve('../templates/src/pages/storykeep/branding.astro'),
@@ -876,10 +888,18 @@ export async function injectTemplateFiles(
876
888
  src: resolve('../templates/src/pages/storykeep/advanced.astro'),
877
889
  dest: 'src/pages/storykeep/advanced.astro',
878
890
  },
891
+ {
892
+ src: resolve('../templates/src/pages/storykeep/shopify.astro'),
893
+ dest: 'src/pages/storykeep/shopify.astro',
894
+ },
879
895
  {
880
896
  src: resolve('../templates/src/pages/maint.astro'),
881
897
  dest: 'src/pages/maint.astro',
882
898
  },
899
+ {
900
+ src: resolve('../templates/custom/shopify/cart.astro'),
901
+ dest: 'src/pages/cart.astro',
902
+ },
883
903
  {
884
904
  src: resolve('../templates/src/pages/404.astro'),
885
905
  dest: 'src/pages/404.astro',
@@ -896,6 +916,14 @@ export async function injectTemplateFiles(
896
916
  src: resolve('../templates/src/pages/sitemap.xml.ts'),
897
917
  dest: 'src/pages/sitemap.xml.ts',
898
918
  },
919
+ {
920
+ src: resolve('../templates/src/pages/api/shopify/getProducts.ts'),
921
+ dest: 'src/pages/api/shopify/getProducts.ts',
922
+ },
923
+ {
924
+ src: resolve('../templates/src/pages/api/shopify/createCart.ts'),
925
+ dest: 'src/pages/api/shopify/createCart.ts',
926
+ },
899
927
  {
900
928
  src: resolve('../templates/src/pages/api/tailwind.ts'),
901
929
  dest: 'src/pages/api/tailwind.ts',
@@ -924,6 +952,38 @@ export async function injectTemplateFiles(
924
952
  },
925
953
 
926
954
  // API Routes
955
+ {
956
+ src: resolve('../templates/src/pages/api/booking/list.ts'),
957
+ dest: 'src/pages/api/booking/list.ts',
958
+ },
959
+ {
960
+ src: resolve('../templates/src/pages/api/booking/metrics.ts'),
961
+ dest: 'src/pages/api/booking/metrics.ts',
962
+ },
963
+ {
964
+ src: resolve('../templates/src/pages/api/booking/cancel.ts'),
965
+ dest: 'src/pages/api/booking/cancel.ts',
966
+ },
967
+ {
968
+ src: resolve('../templates/src/pages/api/booking/confirm.ts'),
969
+ dest: 'src/pages/api/booking/confirm.ts',
970
+ },
971
+ {
972
+ src: resolve('../templates/src/pages/api/booking/release.ts'),
973
+ dest: 'src/pages/api/booking/release.ts',
974
+ },
975
+ {
976
+ src: resolve('../templates/src/pages/api/booking/availability.ts'),
977
+ dest: 'src/pages/api/booking/availability.ts',
978
+ },
979
+ {
980
+ src: resolve('../templates/src/pages/api/booking/hold.ts'),
981
+ dest: 'src/pages/api/booking/hold.ts',
982
+ },
983
+ {
984
+ src: resolve('../templates/src/pages/api/auth/lookup-lead.ts'),
985
+ dest: 'src/pages/api/auth/lookup-lead.ts',
986
+ },
927
987
  {
928
988
  src: resolve('../templates/src/pages/api/auth/profile.ts'),
929
989
  dest: 'src/pages/api/auth/profile.ts',
@@ -1108,6 +1168,12 @@ export async function injectTemplateFiles(
1108
1168
  src: resolve('../templates/src/components/form/brand/SEOSection.tsx'),
1109
1169
  dest: 'src/components/form/brand/SEOSection.tsx',
1110
1170
  },
1171
+ {
1172
+ src: resolve(
1173
+ '../templates/src/components/form/shopify/SchedulingSection.tsx'
1174
+ ),
1175
+ dest: 'src/components/form/shopify/SchedulingSection.tsx',
1176
+ },
1111
1177
 
1112
1178
  // Advanced Configuration Components
1113
1179
  {
@@ -1156,6 +1222,48 @@ export async function injectTemplateFiles(
1156
1222
  ),
1157
1223
  dest: 'src/components/storykeep/Dashboard_Advanced.tsx',
1158
1224
  },
1225
+ {
1226
+ src: resolve(
1227
+ '../templates/src/components/storykeep/Dashboard_Shopify.tsx'
1228
+ ),
1229
+ dest: 'src/components/storykeep/Dashboard_Shopify.tsx',
1230
+ },
1231
+ {
1232
+ src: resolve(
1233
+ '../templates/src/components/storykeep/shopify/ShopifyDashboard.tsx'
1234
+ ),
1235
+ dest: 'src/components/storykeep/shopify/ShopifyDashboard.tsx',
1236
+ },
1237
+ {
1238
+ src: resolve(
1239
+ '../templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx'
1240
+ ),
1241
+ dest: 'src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx',
1242
+ },
1243
+ {
1244
+ src: resolve(
1245
+ '../templates/src/components/storykeep/shopify/ShopifyDashboard_Schedule.tsx'
1246
+ ),
1247
+ dest: 'src/components/storykeep/shopify/ShopifyDashboard_Schedule.tsx',
1248
+ },
1249
+ {
1250
+ src: resolve(
1251
+ '../templates/src/components/storykeep/shopify/ShopifyDashboard_Products.tsx'
1252
+ ),
1253
+ dest: 'src/components/storykeep/shopify/ShopifyDashboard_Products.tsx',
1254
+ },
1255
+ {
1256
+ src: resolve(
1257
+ '../templates/src/components/storykeep/shopify/ShopifyDashboard_Services.tsx'
1258
+ ),
1259
+ dest: 'src/components/storykeep/shopify/ShopifyDashboard_Services.tsx',
1260
+ },
1261
+ {
1262
+ src: resolve(
1263
+ '../templates/src/components/storykeep/shopify/ShopifyDashboard_Search.tsx'
1264
+ ),
1265
+ dest: 'src/components/storykeep/shopify/ShopifyDashboard_Search.tsx',
1266
+ },
1159
1267
  {
1160
1268
  src: resolve(
1161
1269
  '../templates/src/components/storykeep/Dashboard_Analytics.tsx'
@@ -1212,6 +1320,12 @@ export async function injectTemplateFiles(
1212
1320
  ),
1213
1321
  dest: 'src/components/storykeep/controls/content/PaneTable.tsx',
1214
1322
  },
1323
+ {
1324
+ src: resolve(
1325
+ '../templates/src/components/storykeep/controls/content/ProductTable.tsx'
1326
+ ),
1327
+ dest: 'src/components/storykeep/controls/content/ProductTable.tsx',
1328
+ },
1215
1329
  {
1216
1330
  src: resolve(
1217
1331
  '../templates/src/components/storykeep/controls/content/ContentBrowser.tsx'
@@ -2203,6 +2317,51 @@ export async function injectTemplateFiles(
2203
2317
  dest: 'src/utils/customHelpers.ts',
2204
2318
  protected: true,
2205
2319
  },
2320
+ {
2321
+ src: resolve('../templates/custom/shopify/ShopifyProductGrid.tsx'),
2322
+ dest: 'src/custom/shopify/ShopifyProductGrid.tsx',
2323
+ protected: true,
2324
+ },
2325
+ {
2326
+ src: resolve('../templates/custom/shopify/ShopifyServiceList.tsx'),
2327
+ dest: 'src/custom/shopify/ShopifyServiceList.tsx',
2328
+ protected: true,
2329
+ },
2330
+ {
2331
+ src: resolve('../templates/custom/shopify/CartIcon.tsx'),
2332
+ dest: 'src/custom/shopify/CartIcon.tsx',
2333
+ protected: true,
2334
+ },
2335
+ {
2336
+ src: resolve('../templates/custom/shopify/ShopifyCartManager.tsx'),
2337
+ dest: 'src/custom/shopify/ShopifyCartManager.tsx',
2338
+ protected: true,
2339
+ },
2340
+ {
2341
+ src: resolve('../templates/custom/shopify/CartModal.tsx'),
2342
+ dest: 'src/custom/shopify/CartModal.tsx',
2343
+ protected: true,
2344
+ },
2345
+ {
2346
+ src: resolve('../templates/custom/shopify/CheckoutModal.tsx'),
2347
+ dest: 'src/custom/shopify/CheckoutModal.tsx',
2348
+ protected: true,
2349
+ },
2350
+ {
2351
+ src: resolve('../templates/custom/shopify/Cart.tsx'),
2352
+ dest: 'src/custom/shopify/Cart.tsx',
2353
+ protected: true,
2354
+ },
2355
+ {
2356
+ src: resolve('../templates/custom/shopify/ShopifyCheckout.tsx'),
2357
+ dest: 'src/custom/shopify/ShopifyCheckout.tsx',
2358
+ protected: true,
2359
+ },
2360
+ {
2361
+ src: resolve('../templates/custom/shopify/NativeBookingCalendar.tsx'),
2362
+ dest: 'src/custom/shopify/NativeBookingCalendar.tsx',
2363
+ protected: true,
2364
+ },
2206
2365
 
2207
2366
  // Example Components (Conditional)
2208
2367
  ...(config?.includeExamples