astro-tractstack 2.3.4 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-tractstack.js +38 -59
- package/dist/index.js +74 -40
- package/package.json +46 -9
- package/templates/custom/customHelpers.ts +45 -0
- package/templates/custom/minimal/codehooks.ts +13 -0
- package/templates/custom/shopify/Cart.tsx +2 -2
- package/templates/custom/shopify/CartIcon.tsx +1 -1
- package/templates/custom/shopify/CheckoutModal.tsx +3 -3
- package/templates/custom/shopify/ShopifyCartManager.tsx +3 -3
- package/templates/custom/shopify/ShopifyCheckout.tsx +1 -1
- package/templates/custom/shopify/ShopifyProductGrid.tsx +5 -5
- package/templates/custom/shopify/ShopifyServiceList.tsx +5 -5
- package/templates/custom/shopify/shopifyCustomHelper.ts +10 -0
- package/templates/{src/utils/customHelpers.ts → custom/shopify/shopifyHelpers.ts} +0 -74
- package/templates/custom/with-examples/codehooks.ts +15 -0
- package/templates/src/components/Header.astro +1 -1
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +38 -23
- package/templates/src/components/codehooks/EpinetTableView.tsx +5 -2
- package/templates/src/components/codehooks/EpinetWrapper.tsx +10 -5
- package/templates/src/components/codehooks/FeaturedArticle.astro +3 -3
- package/templates/src/components/codehooks/ListContent.astro +3 -3
- package/templates/src/components/codehooks/SearchWidget.tsx +1 -1
- package/templates/src/components/compositor/Node.tsx +13 -2
- package/templates/src/components/compositor/nodes/Pane.tsx +2 -14
- package/templates/src/components/edit/pane/AddPanePanel.tsx +3 -2
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +35 -14
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -1
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +2 -2
- package/templates/src/components/search/SearchResults.tsx +1 -1
- package/templates/src/components/search/SearchWrapper.tsx +1 -1
- package/templates/src/components/storykeep/Dashboard_Analytics.tsx +8 -4
- package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +5 -2
- package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +1 -1
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Sales.tsx +1 -1
- package/templates/src/components/storykeep/state/FetchAnalytics.tsx +8 -4
- package/templates/src/components/storykeep/widgets/Wizard.tsx +4 -2
- package/templates/src/lib/codeHookHelper.ts +156 -0
- package/templates/src/lib/resources.ts +41 -0
- package/templates/src/lib/storyData.ts +1 -2
- package/templates/src/pages/[...slug]/edit.astro +3 -3
- package/templates/src/pages/[...slug].astro +76 -70
- package/templates/src/pages/codehooks/[...hookId].astro +18 -0
- package/templates/src/pages/codehooks/bunny-video.astro +9 -0
- package/templates/src/pages/codehooks/custom-hero.astro +6 -0
- package/templates/src/pages/codehooks/epinet.astro +15 -0
- package/templates/src/pages/codehooks/featured-article.astro +13 -0
- package/templates/src/pages/codehooks/get-crafting.astro +8 -0
- package/templates/src/pages/codehooks/list-content.astro +13 -0
- package/templates/src/pages/codehooks/search-widget.astro +13 -0
- package/templates/src/pages/codehooks/shopify-product-grid.astro +25 -0
- package/templates/src/pages/codehooks/shopify-service-list.astro +25 -0
- package/templates/src/pages/context/[...contextSlug]/edit.astro +3 -3
- package/templates/src/pages/context/[...contextSlug].astro +47 -10
- package/templates/src/pages/sandbox.astro +3 -14
- package/templates/src/stores/analytics.ts +77 -107
- package/utils/inject-files.ts +76 -41
- package/templates/custom/minimal/CodeHook.astro +0 -72
- package/templates/custom/with-examples/CodeHook.astro +0 -81
- package/templates/custom/with-examples/ProductCard.astro +0 -29
- package/templates/custom/with-examples/ProductCardWrapper.astro +0 -43
- package/templates/custom/with-examples/ProductGrid.astro +0 -64
- package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -157
- package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -279
- /package/templates/{src/utils/booking → custom/shopify}/appointmentMode.ts +0 -0
|
@@ -2,52 +2,6 @@ import { getCartItemKey as baseGetCartItemKey } from '@/stores/shopify';
|
|
|
2
2
|
import type { CartItemState, CartKeyParams } from '@/stores/shopify';
|
|
3
3
|
import type { ResourceNode } from '@/types/compositorTypes';
|
|
4
4
|
|
|
5
|
-
// URL Helper: Strip category prefix from slug
|
|
6
|
-
// e.g., "people-bleako" -> "bleako"
|
|
7
|
-
export function getCleanSlug(categorySlug: string, fullSlug: string): string {
|
|
8
|
-
const prefix = `${categorySlug}-`;
|
|
9
|
-
return fullSlug.startsWith(prefix) ? fullSlug.slice(prefix.length) : fullSlug;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Build proper URL for resource
|
|
13
|
-
// e.g., category="people", slug="people-bleako" -> "/people/bleako"
|
|
14
|
-
export function getResourceUrl(categorySlug: string, fullSlug: string): string {
|
|
15
|
-
const cleanSlug = getCleanSlug(categorySlug, fullSlug);
|
|
16
|
-
return `/${categorySlug}/${cleanSlug}`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Image Helper: Placeholder implementation
|
|
20
|
-
export function getResourceImage(
|
|
21
|
-
id: string,
|
|
22
|
-
slug: string,
|
|
23
|
-
category: string
|
|
24
|
-
): string {
|
|
25
|
-
console.log(`please define getResourceImage`, id, slug, category);
|
|
26
|
-
return '/static.jpg';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function getResourceDescription(
|
|
30
|
-
id: string,
|
|
31
|
-
slug: string,
|
|
32
|
-
category: string
|
|
33
|
-
): string | null {
|
|
34
|
-
console.log(`please define getResourceDescription`, id, slug, category);
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Initialize search data - override in custom implementation
|
|
39
|
-
export function initSearch(): void {
|
|
40
|
-
// Default implementation does nothing
|
|
41
|
-
// Override this function in your custom implementation to load search data
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Field Visibility Controls for ResourceForm
|
|
45
|
-
export const resourceFormHideFields = ['shopifyImage'];
|
|
46
|
-
|
|
47
|
-
// Field Formatting Controls for ResourceForm
|
|
48
|
-
// Fields listed here will be treated as JSON objects but rendered as stringified text areas
|
|
49
|
-
export const resourceJsonifyFields = ['shopifyData', 'shopifyImage'];
|
|
50
|
-
|
|
51
5
|
const SERVICES_ATTR_LIMIT = 255;
|
|
52
6
|
|
|
53
7
|
type CheckoutLineAttribute = { key: string; value: string };
|
|
@@ -70,34 +24,6 @@ export type SharedFeeChargeLineSummary = DepositSummary & {
|
|
|
70
24
|
description?: string;
|
|
71
25
|
};
|
|
72
26
|
|
|
73
|
-
export const RESTRICTION_MESSAGES = {
|
|
74
|
-
BOOKING: (duration: number) =>
|
|
75
|
-
`This is a ${duration} minute service. On checkout we'll help you book at your convenience.`,
|
|
76
|
-
TERMS: 'Please review the terms for this item before adding it to your cart.',
|
|
77
|
-
MAX_DURATION: (max: number) =>
|
|
78
|
-
`You cannot book more than ${max} minutes of services in one session.`,
|
|
79
|
-
INCOMPATIBLE_REMOTE:
|
|
80
|
-
'This service cannot be combined with the services already in your cart. Some require remote-only delivery while others can only be delivered in person.',
|
|
81
|
-
DEFAULT_ADD: (title: string) => `${title} has been added to your cart.`,
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// For CartModal.tsx
|
|
85
|
-
export function checkRestrictions(resource: ResourceNode): boolean {
|
|
86
|
-
// 1. Service / Booking Requirement
|
|
87
|
-
// We check for the explicit option payload value used by services
|
|
88
|
-
if (resource.optionsPayload?.bookingLengthMinutes) {
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 2. Final Sale / Terms Check
|
|
93
|
-
// Placeholder: In the future, check for flags like resource.optionsPayload?.finalSale
|
|
94
|
-
// if (resource.optionsPayload?.finalSale) {
|
|
95
|
-
// return true;
|
|
96
|
-
// }
|
|
97
|
-
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
27
|
export function calculateCartDuration(
|
|
102
28
|
cart: Record<string, CartItemState>,
|
|
103
29
|
resources: ResourceNode[]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Static, hand-maintained per-install manifest of available codehook ids.
|
|
2
|
+
// The backend never sees this; it is a frontend build capability list only.
|
|
3
|
+
// To add/remove a hook, edit this array AND add/remove the matching blade at
|
|
4
|
+
// src/pages/codehooks/{id}.astro. Not regenerated automatically.
|
|
5
|
+
export const availableCodeHookIds: string[] = [
|
|
6
|
+
'featured-article',
|
|
7
|
+
'list-content',
|
|
8
|
+
'search-widget',
|
|
9
|
+
'bunny-video',
|
|
10
|
+
'epinet',
|
|
11
|
+
'shopify-product-grid',
|
|
12
|
+
'shopify-service-list',
|
|
13
|
+
'custom-hero',
|
|
14
|
+
'get-crafting',
|
|
15
|
+
];
|
|
@@ -141,7 +141,7 @@ if (hasShopify) {
|
|
|
141
141
|
<div
|
|
142
142
|
class="flex flex-row flex-nowrap justify-between bg-mywhite px-4 pb-3 pt-4 shadow-inner md:px-8"
|
|
143
143
|
>
|
|
144
|
-
<h1 class="truncate text-
|
|
144
|
+
<h1 class="truncate text-2xl text-mydarkgrey">{title}</h1>
|
|
145
145
|
<div class="flex flex-row flex-nowrap items-center gap-x-2">
|
|
146
146
|
{
|
|
147
147
|
!isHome ? (
|
|
@@ -11,7 +11,10 @@ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
|
|
|
11
11
|
import CheckCircleIcon from '@heroicons/react/24/outline/CheckCircleIcon';
|
|
12
12
|
import ChevronLeftIcon from '@heroicons/react/24/outline/ChevronLeftIcon';
|
|
13
13
|
import ChevronRightIcon from '@heroicons/react/24/outline/ChevronRightIcon';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
epinetCustomFilters,
|
|
16
|
+
setEpinetCustomFilters,
|
|
17
|
+
} from '@/stores/analytics';
|
|
15
18
|
import EpinetTableView from './EpinetTableView';
|
|
16
19
|
import { MAX_ANALYTICS_HOURS } from '@/constants';
|
|
17
20
|
import type { AppliedFilter } from '@/stores/analytics';
|
|
@@ -220,7 +223,7 @@ const EpinetDurationSelector = ({
|
|
|
220
223
|
}
|
|
221
224
|
setIsApplying(true);
|
|
222
225
|
try {
|
|
223
|
-
|
|
226
|
+
setEpinetCustomFilters(window.TRACTSTACK_CONFIG?.tenantId || 'default', {
|
|
224
227
|
...$epinetCustomFilters,
|
|
225
228
|
visitorType: localFilters.visitorType,
|
|
226
229
|
selectedUserId: localFilters.selectedUserId,
|
|
@@ -794,10 +797,16 @@ const EpinetDurationSelector = ({
|
|
|
794
797
|
collection={createListCollection({
|
|
795
798
|
items: [
|
|
796
799
|
{ value: '', label: 'Select user' },
|
|
797
|
-
...paginatedUserCounts.map(
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
800
|
+
...paginatedUserCounts.map(
|
|
801
|
+
(user: {
|
|
802
|
+
id: string;
|
|
803
|
+
count: number;
|
|
804
|
+
isKnown: boolean;
|
|
805
|
+
}) => ({
|
|
806
|
+
value: user.id,
|
|
807
|
+
label: `${user.id} (${user.count} events)`,
|
|
808
|
+
})
|
|
809
|
+
),
|
|
801
810
|
],
|
|
802
811
|
})}
|
|
803
812
|
value={
|
|
@@ -844,23 +853,29 @@ const EpinetDurationSelector = ({
|
|
|
844
853
|
Select user
|
|
845
854
|
</Select.ItemText>
|
|
846
855
|
</Select.Item>,
|
|
847
|
-
...paginatedUserCounts.map(
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
856
|
+
...paginatedUserCounts.map(
|
|
857
|
+
(user: {
|
|
858
|
+
id: string;
|
|
859
|
+
count: number;
|
|
860
|
+
isKnown: boolean;
|
|
861
|
+
}) => (
|
|
862
|
+
<Select.Item
|
|
863
|
+
key={user.id}
|
|
864
|
+
item={{
|
|
865
|
+
value: user.id,
|
|
866
|
+
label: `${user.id} (${user.count} events)`,
|
|
867
|
+
}}
|
|
868
|
+
className="epinet-user-select-item cursor-pointer select-none p-2 text-sm text-gray-700 hover:bg-slate-100"
|
|
869
|
+
>
|
|
870
|
+
<Select.ItemText>
|
|
871
|
+
{user.id}{' '}
|
|
872
|
+
<span className="text-xs text-gray-500">
|
|
873
|
+
({user.count} events)
|
|
874
|
+
</span>
|
|
875
|
+
</Select.ItemText>
|
|
876
|
+
</Select.Item>
|
|
877
|
+
)
|
|
878
|
+
),
|
|
864
879
|
]
|
|
865
880
|
) : (
|
|
866
881
|
<div className="p-2 text-sm text-gray-500">
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
2
|
import { useStore } from '@nanostores/react';
|
|
3
3
|
import { classNames } from '@/utils/helpers';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
epinetCustomFilters,
|
|
6
|
+
setEpinetCustomFilters,
|
|
7
|
+
} from '@/stores/analytics';
|
|
5
8
|
import { Accordion } from '@ark-ui/react';
|
|
6
9
|
import ChevronLeftIcon from '@heroicons/react/24/outline/ChevronLeftIcon';
|
|
7
10
|
import ChevronRightIcon from '@heroicons/react/24/outline/ChevronRightIcon';
|
|
@@ -176,7 +179,7 @@ const EpinetTableView = ({
|
|
|
176
179
|
const endTimeUTC = new Date(
|
|
177
180
|
Date.UTC(year, month - 1, day, hour, 59, 59, 999)
|
|
178
181
|
);
|
|
179
|
-
|
|
182
|
+
setEpinetCustomFilters(window.TRACTSTACK_CONFIG?.tenantId || 'default', {
|
|
180
183
|
...$epinetCustomFilters,
|
|
181
184
|
startTimeUTC: startTimeUTC.toISOString(),
|
|
182
185
|
endTimeUTC: endTimeUTC.toISOString(),
|
|
@@ -6,7 +6,12 @@ import {
|
|
|
6
6
|
type ReactNode,
|
|
7
7
|
} from 'react';
|
|
8
8
|
import { useStore } from '@nanostores/react';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
epinetCustomFilters,
|
|
11
|
+
getEpinetCustomFilters,
|
|
12
|
+
setEpinetCustomFilters,
|
|
13
|
+
type AppliedFilter,
|
|
14
|
+
} from '@/stores/analytics';
|
|
10
15
|
import { TractStackAPI } from '@/utils/api';
|
|
11
16
|
import SankeyDiagram from './SankeyDiagram';
|
|
12
17
|
import EpinetDurationSelector from './EpinetDurationSelector';
|
|
@@ -97,7 +102,7 @@ const EpinetWrapper = ({
|
|
|
97
102
|
useEffect(() => {
|
|
98
103
|
const nowUTC = new Date();
|
|
99
104
|
const oneWeekAgoUTC = new Date(nowUTC.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
100
|
-
|
|
105
|
+
setEpinetCustomFilters(window.TRACTSTACK_CONFIG?.tenantId || 'default', {
|
|
101
106
|
enabled: true,
|
|
102
107
|
visitorType: 'all',
|
|
103
108
|
selectedUserId: null,
|
|
@@ -133,7 +138,7 @@ const EpinetWrapper = ({
|
|
|
133
138
|
|
|
134
139
|
const handleBeliefFilterChange = (beliefSlug: string, value: string) => {
|
|
135
140
|
const tenantId = window.TRACTSTACK_CONFIG?.tenantId || 'default';
|
|
136
|
-
const currentFilters =
|
|
141
|
+
const currentFilters = getEpinetCustomFilters();
|
|
137
142
|
let newFilters: AppliedFilter[] = [
|
|
138
143
|
...(currentFilters.appliedFilters || []),
|
|
139
144
|
];
|
|
@@ -151,7 +156,7 @@ const EpinetWrapper = ({
|
|
|
151
156
|
}
|
|
152
157
|
}
|
|
153
158
|
|
|
154
|
-
|
|
159
|
+
setEpinetCustomFilters(tenantId, {
|
|
155
160
|
...currentFilters,
|
|
156
161
|
appliedFilters: newFilters,
|
|
157
162
|
});
|
|
@@ -229,7 +234,7 @@ const EpinetWrapper = ({
|
|
|
229
234
|
error: null,
|
|
230
235
|
isLoading: false,
|
|
231
236
|
});
|
|
232
|
-
|
|
237
|
+
setEpinetCustomFilters(
|
|
233
238
|
window.TRACTSTACK_CONFIG?.tenantId || 'default',
|
|
234
239
|
{
|
|
235
240
|
...$epinetCustomFilters,
|
|
@@ -7,10 +7,10 @@ export interface Props {
|
|
|
7
7
|
options?: string;
|
|
8
8
|
};
|
|
9
9
|
};
|
|
10
|
-
|
|
10
|
+
fullContentMap: FullContentMapItem[];
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const { options,
|
|
13
|
+
const { options, fullContentMap } = Astro.props;
|
|
14
14
|
|
|
15
15
|
let parsedOptions;
|
|
16
16
|
try {
|
|
@@ -22,7 +22,7 @@ try {
|
|
|
22
22
|
|
|
23
23
|
const slug = parsedOptions.slug || '';
|
|
24
24
|
|
|
25
|
-
const featuredStory =
|
|
25
|
+
const featuredStory = fullContentMap.find(
|
|
26
26
|
(item: FullContentMapItem) =>
|
|
27
27
|
item.slug === slug &&
|
|
28
28
|
item.type === 'StoryFragment' &&
|
|
@@ -7,10 +7,10 @@ export interface Props {
|
|
|
7
7
|
options?: string;
|
|
8
8
|
};
|
|
9
9
|
};
|
|
10
|
-
|
|
10
|
+
fullContentMap: FullContentMapItem[];
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const { options,
|
|
13
|
+
const { options, fullContentMap } = Astro.props;
|
|
14
14
|
|
|
15
15
|
// Parse component options
|
|
16
16
|
let parsedOptions;
|
|
@@ -36,7 +36,7 @@ const title = parsedOptions.title;
|
|
|
36
36
|
const bgColor = parsedOptions.bgColor || '';
|
|
37
37
|
|
|
38
38
|
// Filter for valid stories to display
|
|
39
|
-
const validPages =
|
|
39
|
+
const validPages = fullContentMap.filter(
|
|
40
40
|
(item: FullContentMapItem): boolean =>
|
|
41
41
|
item.type === 'StoryFragment' &&
|
|
42
42
|
typeof item.description === 'string' &&
|
|
@@ -149,6 +149,7 @@ export const Node = memo((props: NodeProps) => {
|
|
|
149
149
|
first={true}
|
|
150
150
|
ctx={ctx}
|
|
151
151
|
isContextPane={true}
|
|
152
|
+
isSandboxMode={props.isSandboxMode || false}
|
|
152
153
|
/>
|
|
153
154
|
</>
|
|
154
155
|
);
|
|
@@ -201,7 +202,12 @@ export const Node = memo((props: NodeProps) => {
|
|
|
201
202
|
element = (
|
|
202
203
|
<>
|
|
203
204
|
{first && (
|
|
204
|
-
<AddPanePanel
|
|
205
|
+
<AddPanePanel
|
|
206
|
+
nodeId={props.nodeId}
|
|
207
|
+
first={true}
|
|
208
|
+
ctx={ctx}
|
|
209
|
+
isSandboxMode={props.isSandboxMode || false}
|
|
210
|
+
/>
|
|
205
211
|
)}
|
|
206
212
|
<div className="py-0.5">
|
|
207
213
|
<ConfigPanePanel
|
|
@@ -217,7 +223,12 @@ export const Node = memo((props: NodeProps) => {
|
|
|
217
223
|
{content}
|
|
218
224
|
</PanelVisibilityWrapper>
|
|
219
225
|
</div>
|
|
220
|
-
<AddPanePanel
|
|
226
|
+
<AddPanePanel
|
|
227
|
+
nodeId={props.nodeId}
|
|
228
|
+
first={false}
|
|
229
|
+
ctx={ctx}
|
|
230
|
+
isSandboxMode={props.isSandboxMode || false}
|
|
231
|
+
/>
|
|
221
232
|
</>
|
|
222
233
|
);
|
|
223
234
|
}
|
|
@@ -5,8 +5,6 @@ import { RenderChildren } from './RenderChildren';
|
|
|
5
5
|
import FeaturedArticleSetup from '@/components/codehooks/FeaturedArticleSetup';
|
|
6
6
|
import ListContentSetup from '@/components/codehooks/ListContentSetup';
|
|
7
7
|
import BunnyVideoSetup from '@/components/codehooks/BunnyVideoSetup';
|
|
8
|
-
import { ProductCardSetup } from '@/components/codehooks/ProductCardSetup';
|
|
9
|
-
import { ProductGridSetup } from '@/components/codehooks/ProductGridSetup';
|
|
10
8
|
import { PaneOverlay } from '@/components/compositor/tools/PaneOverlay';
|
|
11
9
|
import type {
|
|
12
10
|
PaneNode,
|
|
@@ -15,13 +13,7 @@ import type {
|
|
|
15
13
|
} from '@/types/compositorTypes';
|
|
16
14
|
import type { NodeProps } from '@/types/nodeProps';
|
|
17
15
|
|
|
18
|
-
const TARGETS = [
|
|
19
|
-
'list-content',
|
|
20
|
-
'featured-article',
|
|
21
|
-
'bunny-video',
|
|
22
|
-
'product-card',
|
|
23
|
-
'product-grid',
|
|
24
|
-
];
|
|
16
|
+
const TARGETS = ['list-content', 'featured-article', 'bunny-video'];
|
|
25
17
|
|
|
26
18
|
const CodeHookContainer = ({
|
|
27
19
|
payload,
|
|
@@ -171,11 +163,7 @@ const Pane = memo(
|
|
|
171
163
|
id={getCtx(props).getNodeSlug(props.nodeId)}
|
|
172
164
|
className={useFlexLayout ? '' : wrapperClasses}
|
|
173
165
|
>
|
|
174
|
-
{codeHookPayload && codeHookTarget === '
|
|
175
|
-
<ProductCardSetup nodeId={props.nodeId} params={codeHookParams} />
|
|
176
|
-
) : codeHookPayload && codeHookTarget === 'product-grid' ? (
|
|
177
|
-
<ProductGridSetup nodeId={props.nodeId} params={codeHookParams} />
|
|
178
|
-
) : codeHookPayload && codeHookTarget === 'featured-article' ? (
|
|
166
|
+
{codeHookPayload && codeHookTarget === 'featured-article' ? (
|
|
179
167
|
<FeaturedArticleSetup
|
|
180
168
|
nodeId={props.nodeId}
|
|
181
169
|
params={codeHookParams}
|
|
@@ -94,13 +94,14 @@ const AddPanePanel = ({
|
|
|
94
94
|
/>
|
|
95
95
|
) : mode === PaneAddMode.REUSE && !isContextPane ? (
|
|
96
96
|
<AddPaneReUsePanel nodeId={nodeId} first={first} setMode={setMode} />
|
|
97
|
-
) : mode === PaneAddMode.CODEHOOK
|
|
97
|
+
) : mode === PaneAddMode.CODEHOOK ? (
|
|
98
98
|
<AddPaneCodeHookPanel
|
|
99
99
|
nodeId={nodeId}
|
|
100
100
|
first={first}
|
|
101
101
|
setMode={setMode}
|
|
102
102
|
isStoryFragment={isStoryFragment}
|
|
103
103
|
isContextPane={isContextPane}
|
|
104
|
+
isSandboxMode={isSandboxMode}
|
|
104
105
|
/>
|
|
105
106
|
) : mode === PaneAddMode.PASTE ? (
|
|
106
107
|
<AddPanePanel_paste
|
|
@@ -139,7 +140,7 @@ const AddPanePanel = ({
|
|
|
139
140
|
)}
|
|
140
141
|
</>
|
|
141
142
|
)}
|
|
142
|
-
{!isTemplate &&
|
|
143
|
+
{!isTemplate && (
|
|
143
144
|
<button
|
|
144
145
|
onClick={() => setMode(PaneAddMode.CODEHOOK)}
|
|
145
146
|
className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white"
|
|
@@ -5,7 +5,11 @@ import { Combobox } from '@ark-ui/react';
|
|
|
5
5
|
import { createListCollection } from '@ark-ui/react/collection';
|
|
6
6
|
import ChevronUpDownIcon from '@heroicons/react/20/solid/ChevronUpDownIcon';
|
|
7
7
|
import CheckIcon from '@heroicons/react/20/solid/CheckIcon';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
brandConfigStore,
|
|
10
|
+
codehookMapStore,
|
|
11
|
+
fullContentMapStore,
|
|
12
|
+
} from '@/stores/storykeep';
|
|
9
13
|
import { getCtx } from '@/stores/nodes';
|
|
10
14
|
import { findUniqueSlug } from '@/utils/helpers';
|
|
11
15
|
import { PaneAddMode, type TemplatePane } from '@/types/compositorTypes';
|
|
@@ -16,6 +20,7 @@ interface AddPaneCodeHookPanelProps {
|
|
|
16
20
|
setMode: (mode: PaneAddMode) => void;
|
|
17
21
|
isStoryFragment?: boolean;
|
|
18
22
|
isContextPane?: boolean;
|
|
23
|
+
isSandboxMode?: boolean;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
const AddPaneCodeHookPanel = ({
|
|
@@ -24,10 +29,13 @@ const AddPaneCodeHookPanel = ({
|
|
|
24
29
|
setMode,
|
|
25
30
|
isStoryFragment = false,
|
|
26
31
|
isContextPane = false,
|
|
32
|
+
isSandboxMode = false,
|
|
27
33
|
}: AddPaneCodeHookPanelProps) => {
|
|
28
34
|
const [selected, setSelected] = useState<string | null>(null);
|
|
29
35
|
const [query, setQuery] = useState('');
|
|
30
36
|
const $contentMap = useStore(fullContentMapStore);
|
|
37
|
+
const brandConfig = useStore(brandConfigStore);
|
|
38
|
+
const hasShopify = brandConfig?.HAS_SHOPIFY === true;
|
|
31
39
|
|
|
32
40
|
const existingSlugs = $contentMap
|
|
33
41
|
.filter((item) => ['Pane', 'StoryFragment'].includes(item.type))
|
|
@@ -39,9 +47,22 @@ const AddPaneCodeHookPanel = ({
|
|
|
39
47
|
|
|
40
48
|
const availableCodeHooks = codehookMapStore.get();
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
const isHookVisibleInPicker = (hookName: string) => {
|
|
51
|
+
if (
|
|
52
|
+
(hookName === 'shopify-product-grid' ||
|
|
53
|
+
hookName === 'shopify-service-list') &&
|
|
54
|
+
!hasShopify
|
|
55
|
+
) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (hookName === 'get-crafting' && !isSandboxMode) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Filter hooks based on search query and tenant/sandbox gates
|
|
43
65
|
const filteredHooks = useMemo(() => {
|
|
44
|
-
// Start with available hooks
|
|
45
66
|
const hooks =
|
|
46
67
|
query === ''
|
|
47
68
|
? [...availableCodeHooks]
|
|
@@ -49,9 +70,8 @@ const AddPaneCodeHookPanel = ({
|
|
|
49
70
|
hook.toLowerCase().includes(query.toLowerCase())
|
|
50
71
|
);
|
|
51
72
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}, [availableCodeHooks, query]);
|
|
73
|
+
return hooks.filter(isHookVisibleInPicker);
|
|
74
|
+
}, [availableCodeHooks, query, hasShopify, isSandboxMode]);
|
|
55
75
|
|
|
56
76
|
// Create collection for Ark UI Combobox
|
|
57
77
|
const collection = useMemo(() => {
|
|
@@ -64,21 +84,22 @@ const AddPaneCodeHookPanel = ({
|
|
|
64
84
|
|
|
65
85
|
const isHookAvailable = (hookName: string) => {
|
|
66
86
|
if (
|
|
67
|
-
(hookName === '
|
|
68
|
-
hookName === 'list-content' ||
|
|
69
|
-
hookName === 'featured-article') &&
|
|
87
|
+
(hookName === 'list-content' || hookName === 'featured-article') &&
|
|
70
88
|
!hasStoryFragments
|
|
71
89
|
) {
|
|
72
|
-
return
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (
|
|
93
|
+
hookName === 'bunny-video' &&
|
|
94
|
+
import.meta.env.PUBLIC_ENABLE_BUNNY !== 'true'
|
|
95
|
+
) {
|
|
96
|
+
return false;
|
|
73
97
|
}
|
|
74
98
|
return true;
|
|
75
99
|
};
|
|
76
100
|
|
|
77
101
|
const getDisplayName = (hookName: string) => {
|
|
78
|
-
if (
|
|
79
|
-
(hookName === 'featured-content' || hookName === 'list-content') &&
|
|
80
|
-
!hasStoryFragments
|
|
81
|
-
) {
|
|
102
|
+
if (hookName === 'list-content' && !hasStoryFragments) {
|
|
82
103
|
return `${hookName} (not yet available; no pages found)`;
|
|
83
104
|
}
|
|
84
105
|
return hookName;
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type MenuNode,
|
|
14
14
|
} from '@/types/compositorTypes';
|
|
15
15
|
import MenuForm from '@/components/storykeep/controls/content/MenuForm';
|
|
16
|
-
import {
|
|
16
|
+
import { getFullContentMap, setTenantFullContentMap } from '@/stores/analytics';
|
|
17
17
|
import type { FullContentMapItem } from '@/types/tractstack';
|
|
18
18
|
|
|
19
19
|
interface StoryFragmentMenuPanelProps {
|
|
@@ -53,7 +53,7 @@ const StoryFragmentMenuPanel = ({
|
|
|
53
53
|
if (!contentMap) {
|
|
54
54
|
const currentContentMap = await api.getContentMapWithTimestamp();
|
|
55
55
|
if (currentContentMap.success && currentContentMap.data) {
|
|
56
|
-
|
|
56
|
+
setTenantFullContentMap(tenantId, currentContentMap.data);
|
|
57
57
|
setContentMap(currentContentMap.data.data);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
2
|
import MagnifyingGlassIcon from '@heroicons/react/24/outline/MagnifyingGlassIcon';
|
|
3
|
-
import { initSearch } from '@/
|
|
3
|
+
import { initSearch } from '@/custom/customHelpers';
|
|
4
4
|
import SearchModal from './SearchModal';
|
|
5
5
|
import type { FullContentMapItem } from '@/types/tractstack';
|
|
6
6
|
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { useState, useCallback, useMemo, Component } from 'react';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
3
|
import { useStore } from '@nanostores/react';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
epinetCustomFilters,
|
|
6
|
+
getEpinetCustomFilters,
|
|
7
|
+
setEpinetCustomFilters,
|
|
8
|
+
} from '@/stores/analytics';
|
|
5
9
|
import { classNames } from '@/utils/helpers';
|
|
6
10
|
import ArrowDownTrayIcon from '@heroicons/react/24/outline/ArrowDownTrayIcon';
|
|
7
11
|
import DashboardActivity from './Dashboard_Activity';
|
|
@@ -167,7 +171,7 @@ export default function StoryKeepDashboard_Analytics({
|
|
|
167
171
|
|
|
168
172
|
const handleBeliefFilterChange = (beliefSlug: string, value: string) => {
|
|
169
173
|
const tenantId = window.TRACTSTACK_CONFIG?.tenantId || 'default';
|
|
170
|
-
const currentFilters =
|
|
174
|
+
const currentFilters = getEpinetCustomFilters();
|
|
171
175
|
let newFilters = [...(currentFilters.appliedFilters || [])];
|
|
172
176
|
|
|
173
177
|
if (value === 'All') {
|
|
@@ -183,7 +187,7 @@ export default function StoryKeepDashboard_Analytics({
|
|
|
183
187
|
}
|
|
184
188
|
}
|
|
185
189
|
|
|
186
|
-
|
|
190
|
+
setEpinetCustomFilters(tenantId, {
|
|
187
191
|
...currentFilters,
|
|
188
192
|
appliedFilters: newFilters,
|
|
189
193
|
});
|
|
@@ -220,7 +224,7 @@ export default function StoryKeepDashboard_Analytics({
|
|
|
220
224
|
nowUTC.getTime() - hoursBack * 60 * 60 * 1000
|
|
221
225
|
);
|
|
222
226
|
|
|
223
|
-
|
|
227
|
+
setEpinetCustomFilters(window.TRACTSTACK_CONFIG?.tenantId || 'default', {
|
|
224
228
|
...$epinetCustomFilters,
|
|
225
229
|
enabled: true,
|
|
226
230
|
startTimeUTC: startTimeUTC.toISOString(),
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { useState, useEffect, useRef } from 'react';
|
|
2
2
|
import { Switch } from '@ark-ui/react';
|
|
3
3
|
import { useStore } from '@nanostores/react';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
epinetCustomFilters,
|
|
6
|
+
setEpinetCustomFilters,
|
|
7
|
+
} from '@/stores/analytics';
|
|
5
8
|
import { classNames } from '@/utils/helpers';
|
|
6
9
|
import type { FullContentMapItem } from '@/types/tractstack';
|
|
7
10
|
|
|
@@ -104,7 +107,7 @@ const ContentBrowser = ({
|
|
|
104
107
|
const setStandardDuration = (hours: number) => {
|
|
105
108
|
const nowUTC = new Date();
|
|
106
109
|
const startTimeUTC = new Date(nowUTC.getTime() - hours * 60 * 60 * 1000);
|
|
107
|
-
|
|
110
|
+
setEpinetCustomFilters(window.TRACTSTACK_CONFIG?.tenantId || 'default', {
|
|
108
111
|
...$epinetCustomFilters,
|
|
109
112
|
enabled: true,
|
|
110
113
|
startTimeUTC: startTimeUTC.toISOString(),
|