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.
- package/README.md +1 -1
- package/bin/create-tractstack.js +2 -2
- package/dist/index.js +177 -18
- package/package.json +4 -2
- package/templates/custom/minimal/CodeHook.astro +22 -5
- package/templates/custom/shopify/Cart.tsx +372 -0
- package/templates/custom/shopify/CartIcon.tsx +47 -0
- package/templates/custom/shopify/CartModal.tsx +63 -0
- package/templates/custom/shopify/CheckoutModal.tsx +576 -0
- package/templates/custom/shopify/NativeBookingCalendar.tsx +375 -0
- package/templates/custom/shopify/ShopifyCartManager.tsx +200 -0
- package/templates/custom/shopify/ShopifyCheckout.tsx +167 -0
- package/templates/custom/shopify/ShopifyProductGrid.tsx +247 -0
- package/templates/custom/shopify/ShopifyServiceList.tsx +135 -0
- package/templates/custom/shopify/cart.astro +23 -0
- package/templates/custom/with-examples/CodeHook.astro +17 -1
- package/templates/custom/with-examples/ProductGrid.astro +1 -1
- package/templates/src/client/app.js +4 -2
- package/templates/src/components/Footer.astro +4 -4
- package/templates/src/components/Header.astro +44 -12
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +3 -3
- package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +2 -2
- package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +2 -2
- package/templates/src/components/edit/pane/steps/AiLibraryCopyStep.tsx +3 -3
- package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +2 -2
- package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +7 -7
- package/templates/src/components/form/advanced/APIConfigSection.tsx +407 -38
- package/templates/src/components/form/shopify/SchedulingSection.tsx +354 -0
- package/templates/src/components/storykeep/Dashboard.tsx +18 -4
- package/templates/src/components/storykeep/Dashboard_Advanced.tsx +1 -0
- package/templates/src/components/storykeep/Dashboard_Content.tsx +5 -96
- package/templates/src/components/storykeep/Dashboard_Shopify.tsx +668 -0
- package/templates/src/components/storykeep/StoryKeepBackdrop.astro +43 -23
- package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +14 -5
- package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +0 -14
- package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +36 -13
- package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +5 -2
- package/templates/src/components/storykeep/controls/content/ManageContent.tsx +4 -11
- package/templates/src/components/storykeep/controls/content/MenuTable.tsx +14 -5
- package/templates/src/components/storykeep/controls/content/ProductTable.tsx +333 -0
- package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +9 -5
- package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +108 -8
- package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +13 -4
- package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +14 -5
- package/templates/src/components/storykeep/shopify/ShopifyDashboard.tsx +111 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx +393 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Products.tsx +46 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Schedule.tsx +78 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Search.tsx +55 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Services.tsx +47 -0
- package/templates/src/lib/resources.ts +11 -21
- package/templates/src/pages/api/auth/lookup-lead.ts +72 -0
- package/templates/src/pages/api/booking/availability.ts +72 -0
- package/templates/src/pages/api/booking/cancel.ts +73 -0
- package/templates/src/pages/api/booking/confirm.ts +82 -0
- package/templates/src/pages/api/booking/hold.ts +75 -0
- package/templates/src/pages/api/booking/list.ts +66 -0
- package/templates/src/pages/api/booking/metrics.ts +60 -0
- package/templates/src/pages/api/booking/release.ts +76 -0
- package/templates/src/pages/api/sandbox.ts +2 -2
- package/templates/src/pages/api/shopify/createCart.ts +69 -0
- package/templates/src/pages/api/shopify/getProducts.ts +64 -0
- package/templates/src/pages/storykeep/login.astro +26 -24
- package/templates/src/pages/storykeep/logout.astro +1 -10
- package/templates/src/pages/storykeep/manage.astro +69 -0
- package/templates/src/pages/storykeep/{content.astro → pages.astro} +4 -8
- package/templates/src/pages/storykeep/shopify.astro +101 -0
- package/templates/src/stores/navigation.ts +3 -42
- package/templates/src/stores/nodes.ts +3 -1
- package/templates/src/stores/resources.ts +7 -10
- package/templates/src/stores/shopify.ts +266 -0
- package/templates/src/types/tractstack.ts +75 -0
- package/templates/src/utils/api/advancedConfig.ts +7 -1
- package/templates/src/utils/api/advancedHelpers.ts +87 -7
- package/templates/src/utils/api/bookingHelpers.ts +125 -0
- package/templates/src/utils/api/brandHelpers.ts +14 -0
- package/templates/src/utils/api/resourceConfig.ts +13 -5
- package/templates/src/utils/auth.ts +29 -9
- package/templates/src/utils/compositor/aiGeneration.ts +3 -3
- package/templates/src/utils/compositor/aiPaneParser.ts +2 -2
- package/templates/src/utils/customHelpers.ts +49 -0
- package/templates/src/utils/helpers.ts +59 -0
- package/templates/src/utils/profileStorage.ts +5 -0
- package/templates/src/utils/tenantResolver.ts +2 -1
- 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(
|
|
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
|
|
120
|
-
|
|
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(
|
|
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
|
|
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: '
|
|
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/
|
|
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-
|
|
774
|
+
mobile: 'text-cyan-600 hover:text-cyan-500 font-bold',
|
|
775
775
|
},
|
|
776
776
|
button: {
|
|
777
777
|
mobile:
|
|
778
|
-
'rounded-md bg-
|
|
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:
|
|
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
|
|
package/utils/inject-files.ts
CHANGED
|
@@ -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/
|
|
869
|
-
dest: 'src/pages/storykeep/
|
|
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
|