astro-tractstack 2.0.40 → 2.0.42
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/dist/index.js +8 -2
- package/package.json +1 -1
- package/templates/src/components/Header.astro +1 -0
- package/templates/src/components/compositor/Node.tsx +4 -1
- package/templates/src/components/compositor/preview/PanesPreviewGenerator.tsx +13 -13
- package/templates/src/components/edit/SettingsPanel.tsx +1 -3
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +6 -10
- package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +6 -2
- package/templates/src/components/edit/pane/PanePanel_path.tsx +4 -3
- package/templates/src/components/edit/panels/StyleParentPanel.tsx +0 -2
- package/templates/src/components/edit/state/SaveModal.tsx +250 -79
- package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +27 -16
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +5 -7
- package/templates/src/components/edit/widgets/BeliefWidget.tsx +4 -1
- package/templates/src/components/edit/widgets/IdentifyAsWidget.tsx +5 -1
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +5 -1
- package/templates/src/components/edit/widgets/ToggleWidget.tsx +4 -1
- package/templates/src/components/fields/BackgroundImage.tsx +4 -1
- package/templates/src/components/fields/ImageUpload.tsx +4 -1
- package/templates/src/components/form/ActionBuilderField.tsx +5 -1
- package/templates/src/components/storykeep/Dashboard_Analytics.tsx +4 -2
- package/templates/src/components/storykeep/state/BrandingWrapper.tsx +13 -1
- package/templates/src/components/storykeep/widgets/HydrateWizard.tsx +84 -0
- package/templates/src/components/storykeep/widgets/{SetupWizard.tsx → InitWizard.tsx} +4 -3
- package/templates/src/components/widgets/Impression.tsx +3 -1
- package/templates/src/hooks/useSearch.ts +5 -3
- package/templates/src/layouts/Layout.astro +1 -23
- package/templates/src/pages/[...slug]/edit.astro +0 -1
- package/templates/src/pages/api/auth/decode.ts +2 -4
- package/templates/src/pages/api/auth/login.ts +4 -5
- package/templates/src/pages/api/auth/logout.ts +22 -7
- package/templates/src/pages/api/auth/profile.ts +4 -2
- package/templates/src/pages/api/sandbox.ts +3 -5
- package/templates/src/pages/api/tailwind.ts +6 -9
- package/templates/src/pages/storykeep/branding.astro +18 -1
- package/templates/src/pages/storykeep/init.astro +2 -2
- package/templates/src/stores/analytics.ts +5 -14
- package/templates/src/stores/nodes.ts +1 -6
- package/templates/src/stores/orphanAnalysis.ts +5 -40
- package/templates/src/types/compositorTypes.ts +1 -1
- package/templates/src/types/tractstack.ts +2 -0
- package/templates/src/utils/actions/actionButton.ts +3 -1
- package/templates/src/utils/api/brandHelpers.ts +1 -0
- package/templates/src/utils/api/setupHelpers.ts +177 -20
- package/templates/src/utils/api.ts +14 -26
- package/templates/src/utils/compositor/nodesHelper.ts +5 -1
- package/templates/src/utils/tenantResolver.ts +1 -1
- package/utils/inject-files.ts +8 -2
|
@@ -26,7 +26,13 @@ import {
|
|
|
26
26
|
type StoryFragmentNode,
|
|
27
27
|
} from '@/types/compositorTypes';
|
|
28
28
|
|
|
29
|
-
const StoryFragmentConfigPanel = ({
|
|
29
|
+
const StoryFragmentConfigPanel = ({
|
|
30
|
+
nodeId,
|
|
31
|
+
isSandboxMode,
|
|
32
|
+
}: {
|
|
33
|
+
nodeId: string;
|
|
34
|
+
isSandboxMode: boolean;
|
|
35
|
+
}) => {
|
|
30
36
|
const brandColors = brandConfigStore.get()?.BRAND_COLOURS || '';
|
|
31
37
|
const [isNodeAvailable, setIsNodeAvailable] = useState(false);
|
|
32
38
|
const [storyfragmentNode, setStoryfragmentNode] =
|
|
@@ -185,21 +191,26 @@ const StoryFragmentConfigPanel = ({ nodeId }: { nodeId: string }) => {
|
|
|
185
191
|
<div className="mb-4">
|
|
186
192
|
<div className="w-full rounded-b-md bg-white p-4">
|
|
187
193
|
<div className="flex flex-wrap items-center gap-2">
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
194
|
+
{!isSandboxMode && (
|
|
195
|
+
<>
|
|
196
|
+
<button
|
|
197
|
+
onClick={() => setMode(StoryFragmentMode.OG)}
|
|
198
|
+
className="text-md min-h-9 rounded border border-cyan-200 bg-white px-3 text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white focus:bg-cyan-700 focus:text-white"
|
|
199
|
+
>
|
|
200
|
+
Title:{' '}
|
|
201
|
+
<span className="font-bold">{storyfragmentNode.title}</span>
|
|
202
|
+
</button>
|
|
203
|
+
<button
|
|
204
|
+
onClick={() => setMode(StoryFragmentMode.SLUG)}
|
|
205
|
+
className="text-md h-9 rounded border border-cyan-200 bg-white px-3 text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white focus:bg-cyan-700 focus:text-white"
|
|
206
|
+
>
|
|
207
|
+
Slug:{' '}
|
|
208
|
+
<span className="font-bold">{storyfragmentNode.slug}</span>
|
|
209
|
+
</button>
|
|
210
|
+
</>
|
|
211
|
+
)}
|
|
212
|
+
|
|
213
|
+
{!isSandboxMode && !isTemplate && (
|
|
203
214
|
<>
|
|
204
215
|
<button
|
|
205
216
|
onClick={() => setMode(StoryFragmentMode.MENU)}
|
|
@@ -39,20 +39,20 @@ const StoryFragmentMenuPanel = ({
|
|
|
39
39
|
const [selectedMenu, setSelectedMenu] = useState<MenuNode | null>(null);
|
|
40
40
|
const [showMenuEditor, setShowMenuEditor] = useState(false);
|
|
41
41
|
const [contentMap, setContentMap] = useState<FullContentMapItem[]>([]);
|
|
42
|
+
const tenantId =
|
|
43
|
+
window.TRACTSTACK_CONFIG?.tenantId ||
|
|
44
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
45
|
+
'default';
|
|
42
46
|
|
|
43
47
|
useEffect(() => {
|
|
44
48
|
const fetchData = async () => {
|
|
45
49
|
try {
|
|
46
|
-
const api = new TractStackAPI();
|
|
50
|
+
const api = new TractStackAPI(tenantId);
|
|
47
51
|
|
|
48
52
|
// Get current content map first if we haven't already
|
|
49
53
|
if (!contentMap) {
|
|
50
54
|
const currentContentMap = await api.getContentMapWithTimestamp();
|
|
51
55
|
if (currentContentMap.success && currentContentMap.data) {
|
|
52
|
-
const tenantId =
|
|
53
|
-
window.TRACTSTACK_CONFIG?.tenantId ||
|
|
54
|
-
import.meta.env.PUBLIC_TENANTID ||
|
|
55
|
-
'default';
|
|
56
56
|
fullContentMapStore.set(tenantId, currentContentMap.data);
|
|
57
57
|
setContentMap(currentContentMap.data.data);
|
|
58
58
|
}
|
|
@@ -240,8 +240,6 @@ const StoryFragmentMenuPanel = ({
|
|
|
240
240
|
setShowMenuEditor(false);
|
|
241
241
|
if (saved) {
|
|
242
242
|
try {
|
|
243
|
-
const tenantId =
|
|
244
|
-
import.meta.env.PUBLIC_TENANTID || 'default';
|
|
245
243
|
const refreshedContentMap =
|
|
246
244
|
await getFullContentMap(tenantId);
|
|
247
245
|
setContentMap(refreshedContentMap);
|
|
@@ -14,6 +14,10 @@ export default function BeliefWidget({ node, onUpdate }: BeliefWidgetProps) {
|
|
|
14
14
|
const [currentScaleType, setCurrentScaleType] = useState<string>('');
|
|
15
15
|
const [currentPrompt, setCurrentPrompt] = useState<string>('');
|
|
16
16
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
17
|
+
const tenantId =
|
|
18
|
+
window.TRACTSTACK_CONFIG?.tenantId ||
|
|
19
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
20
|
+
'default';
|
|
17
21
|
|
|
18
22
|
// Get parameter metadata from the widgetMeta constant
|
|
19
23
|
const widgetInfo = widgetMeta.belief;
|
|
@@ -43,7 +47,6 @@ export default function BeliefWidget({ node, onUpdate }: BeliefWidgetProps) {
|
|
|
43
47
|
try {
|
|
44
48
|
const goBackend =
|
|
45
49
|
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
46
|
-
const tenantId = import.meta.env.PUBLIC_TENANTID || 'default';
|
|
47
50
|
|
|
48
51
|
// Step 1: Get all belief IDs
|
|
49
52
|
const idsResponse = await fetch(`${goBackend}/api/v1/nodes/beliefs`, {
|
|
@@ -16,6 +16,10 @@ export default function IdentifyAsWidget({
|
|
|
16
16
|
const [selectedBeliefTag, setSelectedBeliefTag] = useState<string>('');
|
|
17
17
|
const [targetValues, setTargetValues] = useState<string[]>([]);
|
|
18
18
|
const [currentPrompt, setCurrentPrompt] = useState<string>('');
|
|
19
|
+
const tenantId =
|
|
20
|
+
window.TRACTSTACK_CONFIG?.tenantId ||
|
|
21
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
22
|
+
'default';
|
|
19
23
|
|
|
20
24
|
// Sync state with node.codeHookParams
|
|
21
25
|
useEffect(() => {
|
|
@@ -54,7 +58,7 @@ export default function IdentifyAsWidget({
|
|
|
54
58
|
useEffect(() => {
|
|
55
59
|
const fetchData = async () => {
|
|
56
60
|
try {
|
|
57
|
-
const api = new TractStackAPI();
|
|
61
|
+
const api = new TractStackAPI(tenantId);
|
|
58
62
|
|
|
59
63
|
// Step 1: Get all belief IDs
|
|
60
64
|
const idsResponse = await api.get('/api/v1/nodes/beliefs');
|
|
@@ -253,6 +253,10 @@ export default function InteractiveDisclosureWidget({
|
|
|
253
253
|
node,
|
|
254
254
|
onUpdate,
|
|
255
255
|
}: InteractiveDisclosureWidgetProps) {
|
|
256
|
+
const tenantId =
|
|
257
|
+
window.TRACTSTACK_CONFIG?.tenantId ||
|
|
258
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
259
|
+
'default';
|
|
256
260
|
const [mode, setMode] = useState<'belief' | 'open'>('belief');
|
|
257
261
|
const [beliefs, setBeliefs] = useState<BeliefNode[]>([]);
|
|
258
262
|
const [selectedBeliefTag, setSelectedBeliefTag] = useState<string>('');
|
|
@@ -367,7 +371,7 @@ export default function InteractiveDisclosureWidget({
|
|
|
367
371
|
useEffect(() => {
|
|
368
372
|
const fetchData = async () => {
|
|
369
373
|
try {
|
|
370
|
-
const api = new TractStackAPI();
|
|
374
|
+
const api = new TractStackAPI(tenantId);
|
|
371
375
|
const {
|
|
372
376
|
data: { beliefIds },
|
|
373
377
|
} = await api.get('/api/v1/nodes/beliefs');
|
|
@@ -14,6 +14,10 @@ export default function ToggleWidget({ node, onUpdate }: ToggleWidgetProps) {
|
|
|
14
14
|
const [currentPrompt, setCurrentPrompt] = useState<string>('');
|
|
15
15
|
const [currentScale, setCurrentScale] = useState<string>('');
|
|
16
16
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
17
|
+
const tenantId =
|
|
18
|
+
window.TRACTSTACK_CONFIG?.tenantId ||
|
|
19
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
20
|
+
'default';
|
|
17
21
|
|
|
18
22
|
const widgetInfo = widgetMeta.toggle;
|
|
19
23
|
|
|
@@ -38,7 +42,6 @@ export default function ToggleWidget({ node, onUpdate }: ToggleWidgetProps) {
|
|
|
38
42
|
try {
|
|
39
43
|
const goBackend =
|
|
40
44
|
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
41
|
-
const tenantId = import.meta.env.PUBLIC_TENANTID || 'default';
|
|
42
45
|
|
|
43
46
|
const idsResponse = await fetch(`${goBackend}/api/v1/nodes/beliefs`, {
|
|
44
47
|
headers: {
|
|
@@ -42,13 +42,16 @@ const BackgroundImage = ({ paneId, onUpdate }: BackgroundImageProps) => {
|
|
|
42
42
|
desktop: false,
|
|
43
43
|
});
|
|
44
44
|
const [localAltDescription, setLocalAltDescription] = useState<string>('');
|
|
45
|
+
const tenantId =
|
|
46
|
+
window.TRACTSTACK_CONFIG?.tenantId ||
|
|
47
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
48
|
+
'default';
|
|
45
49
|
|
|
46
50
|
useEffect(() => {
|
|
47
51
|
const loadFiles = async () => {
|
|
48
52
|
try {
|
|
49
53
|
const goBackend =
|
|
50
54
|
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
51
|
-
const tenantId = import.meta.env.PUBLIC_TENANTID || 'default';
|
|
52
55
|
|
|
53
56
|
// First, get all file IDs
|
|
54
57
|
const idsResponse = await fetch(`${goBackend}/api/v1/nodes/files`, {
|
|
@@ -48,6 +48,10 @@ export const ImageUpload = ({
|
|
|
48
48
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
49
49
|
const comboboxRef = useRef<HTMLDivElement>(null);
|
|
50
50
|
const { openAbove, maxHeight } = useDropdownDirection(comboboxRef);
|
|
51
|
+
const tenantId =
|
|
52
|
+
window.TRACTSTACK_CONFIG?.tenantId ||
|
|
53
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
54
|
+
'default';
|
|
51
55
|
|
|
52
56
|
// Find the current image node
|
|
53
57
|
const currentImageNode = nodeId
|
|
@@ -61,7 +65,6 @@ export const ImageUpload = ({
|
|
|
61
65
|
try {
|
|
62
66
|
const goBackend =
|
|
63
67
|
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
64
|
-
const tenantId = import.meta.env.PUBLIC_TENANTID || 'default';
|
|
65
68
|
|
|
66
69
|
// First, get all file IDs
|
|
67
70
|
const idsResponse = await fetch(`${goBackend}/api/v1/nodes/files`, {
|
|
@@ -46,11 +46,15 @@ export default function ActionBuilderField({
|
|
|
46
46
|
const [command, setCommand] = useState<ActionCommand>('goto');
|
|
47
47
|
const [params, setParams] = useState('');
|
|
48
48
|
const [beliefs, setBeliefs] = useState<BeliefNode[]>([]);
|
|
49
|
+
const tenantId =
|
|
50
|
+
window.TRACTSTACK_CONFIG?.tenantId ||
|
|
51
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
52
|
+
'default';
|
|
49
53
|
|
|
50
54
|
useEffect(() => {
|
|
51
55
|
const fetchData = async () => {
|
|
52
56
|
try {
|
|
53
|
-
const api = new TractStackAPI();
|
|
57
|
+
const api = new TractStackAPI(tenantId);
|
|
54
58
|
const {
|
|
55
59
|
data: { beliefIds },
|
|
56
60
|
} = await api.get('/api/v1/nodes/beliefs');
|
|
@@ -178,14 +178,16 @@ export default function StoryKeepDashboard_Analytics({
|
|
|
178
178
|
setIsDownloading(true);
|
|
179
179
|
|
|
180
180
|
const config = window.TRACTSTACK_CONFIG;
|
|
181
|
+
const backendUrl =
|
|
182
|
+
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
181
183
|
const response = await fetch(
|
|
182
|
-
`${
|
|
184
|
+
`${backendUrl}/api/v1/admin/leads/download`,
|
|
183
185
|
{
|
|
184
186
|
method: 'GET',
|
|
185
187
|
headers: {
|
|
186
188
|
'X-Tenant-ID': config?.tenantId || 'default',
|
|
187
189
|
},
|
|
188
|
-
credentials: 'include',
|
|
190
|
+
credentials: 'include',
|
|
189
191
|
}
|
|
190
192
|
);
|
|
191
193
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import StoryKeepDashboard from '../Dashboard';
|
|
3
3
|
import StoryKeepDashboard_Branding from '../Dashboard_Branding';
|
|
4
|
+
import HydrateWizard from '../widgets/HydrateWizard';
|
|
4
5
|
import type { FullContentMapItem, BrandConfig } from '@/types/tractstack';
|
|
6
|
+
import type { LoadData } from '@/types/compositorTypes';
|
|
5
7
|
|
|
6
8
|
interface BrandingPageWrapperProps {
|
|
7
9
|
fullContentMap: FullContentMapItem[];
|
|
@@ -9,6 +11,7 @@ interface BrandingPageWrapperProps {
|
|
|
9
11
|
role: string | null;
|
|
10
12
|
initializing: boolean;
|
|
11
13
|
initialBrandConfig: BrandConfig;
|
|
14
|
+
initialSuitcase?: LoadData | null;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
export default function BrandingPageWrapper({
|
|
@@ -17,11 +20,20 @@ export default function BrandingPageWrapper({
|
|
|
17
20
|
role,
|
|
18
21
|
initializing,
|
|
19
22
|
initialBrandConfig,
|
|
23
|
+
initialSuitcase,
|
|
20
24
|
}: BrandingPageWrapperProps) {
|
|
21
|
-
// Manage shared brandConfig state at this level
|
|
22
25
|
const [brandConfig, setBrandConfig] =
|
|
23
26
|
useState<BrandConfig>(initialBrandConfig);
|
|
24
27
|
|
|
28
|
+
if (initialBrandConfig.HAS_HYDRATION_TOKEN && initialSuitcase) {
|
|
29
|
+
return (
|
|
30
|
+
<HydrateWizard
|
|
31
|
+
initialSuitcase={initialSuitcase}
|
|
32
|
+
fullContentMap={fullContentMap}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
return (
|
|
26
38
|
<>
|
|
27
39
|
<StoryKeepDashboard
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useEffect, useState, useRef } from 'react';
|
|
2
|
+
import { prepareHydrationContext } from '@/utils/api/setupHelpers';
|
|
3
|
+
import SaveModal from '@/components/edit/state/SaveModal';
|
|
4
|
+
import type { FullContentMapItem } from '@/types/tractstack';
|
|
5
|
+
import type { LoadData } from '@/types/compositorTypes';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
initialSuitcase: LoadData;
|
|
9
|
+
fullContentMap: FullContentMapItem[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function HydrateWizard({
|
|
13
|
+
initialSuitcase,
|
|
14
|
+
fullContentMap,
|
|
15
|
+
}: Props) {
|
|
16
|
+
const [status, setStatus] = useState<'preparing' | 'ready' | 'error'>(
|
|
17
|
+
'preparing'
|
|
18
|
+
);
|
|
19
|
+
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
20
|
+
const hasInitialized = useRef(false);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (hasInitialized.current) return;
|
|
24
|
+
hasInitialized.current = true;
|
|
25
|
+
|
|
26
|
+
const timer = setTimeout(() => {
|
|
27
|
+
try {
|
|
28
|
+
prepareHydrationContext(initialSuitcase, fullContentMap);
|
|
29
|
+
setStatus('ready');
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.error('Hydration preparation failed:', err);
|
|
32
|
+
setErrorMessage(err instanceof Error ? err.message : String(err));
|
|
33
|
+
setStatus('error');
|
|
34
|
+
}
|
|
35
|
+
}, 100);
|
|
36
|
+
|
|
37
|
+
return () => clearTimeout(timer);
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
if (status === 'error') {
|
|
41
|
+
return (
|
|
42
|
+
<div className="mx-auto max-w-2xl p-6 text-center">
|
|
43
|
+
<div className="rounded-lg bg-white p-12 shadow-lg">
|
|
44
|
+
<h2 className="text-xl font-bold text-red-600">Setup Failed</h2>
|
|
45
|
+
<p className="mt-2 text-gray-600">{errorMessage}</p>
|
|
46
|
+
<div className="mt-6">
|
|
47
|
+
<a
|
|
48
|
+
href="/storykeep"
|
|
49
|
+
className="rounded bg-gray-200 px-4 py-2 text-gray-800 hover:bg-gray-300"
|
|
50
|
+
>
|
|
51
|
+
Return to Dashboard
|
|
52
|
+
</a>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (status === 'ready') {
|
|
60
|
+
return (
|
|
61
|
+
<SaveModal
|
|
62
|
+
show={true}
|
|
63
|
+
slug="hello"
|
|
64
|
+
isContext={false}
|
|
65
|
+
onClose={() => {}}
|
|
66
|
+
hydrate={true}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className="mx-auto max-w-2xl p-6 text-center">
|
|
73
|
+
<div className="rounded-lg bg-white p-12 shadow-lg">
|
|
74
|
+
<div className="mb-4 flex h-16 justify-center">
|
|
75
|
+
<div className="h-12 w-12 animate-spin rounded-full border-4 border-gray-200 border-t-cyan-600"></div>
|
|
76
|
+
</div>
|
|
77
|
+
<h2 className="text-2xl font-bold text-gray-900">
|
|
78
|
+
Preparing installation...
|
|
79
|
+
</h2>
|
|
80
|
+
<p className="mt-2 text-gray-600">Unpacking suitcase content</p>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
initializeSystem,
|
|
10
10
|
} from '@/utils/api/setupHelpers';
|
|
11
11
|
|
|
12
|
-
export default function
|
|
12
|
+
export default function InitWizard() {
|
|
13
13
|
const formState = useFormState({
|
|
14
14
|
initialData: initialSetupState,
|
|
15
15
|
validator: validateSetup,
|
|
@@ -17,8 +17,9 @@ export default function SetupWizard() {
|
|
|
17
17
|
onSave: async (data) => {
|
|
18
18
|
try {
|
|
19
19
|
await initializeSystem(data);
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
window.location.href = '/storykeep';
|
|
22
|
+
}, 1000);
|
|
22
23
|
return data;
|
|
23
24
|
} catch (error) {
|
|
24
25
|
console.error('Installation failed:', error);
|
|
@@ -47,8 +47,10 @@ const Impression = ({ payload, currentPage, config }: ImpressionProps) => {
|
|
|
47
47
|
beliefValue: 'CLICKED',
|
|
48
48
|
paneId: payload.parentId || '',
|
|
49
49
|
};
|
|
50
|
+
const backendUrl =
|
|
51
|
+
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
50
52
|
|
|
51
|
-
await fetch(`${
|
|
53
|
+
await fetch(`${backendUrl}/api/v1/state`, {
|
|
52
54
|
method: 'POST',
|
|
53
55
|
headers: {
|
|
54
56
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
@@ -31,20 +31,22 @@ export function useSearch(): UseSearchReturn {
|
|
|
31
31
|
const [suggestions, setSuggestions] = useState<DiscoverySuggestion[]>([]);
|
|
32
32
|
const [isDiscovering, setIsDiscovering] = useState(false);
|
|
33
33
|
const [discoverError, setDiscoverError] = useState<string | null>(null);
|
|
34
|
+
const tenantId =
|
|
35
|
+
(typeof window !== 'undefined' && window.TRACTSTACK_CONFIG?.tenantId) ||
|
|
36
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
37
|
+
'default';
|
|
34
38
|
|
|
35
|
-
// Retrieve state
|
|
36
39
|
const [searchResults, setSearchResults] = useState<CategorizedResults | null>(
|
|
37
40
|
null
|
|
38
41
|
);
|
|
39
42
|
const [isRetrieving, setIsRetrieving] = useState(false);
|
|
40
43
|
const [retrieveError, setRetrieveError] = useState<string | null>(null);
|
|
41
44
|
|
|
42
|
-
// --- REVISED STATE FOR SEARCH LOGIC ---
|
|
43
45
|
const searchTimerRef = useRef<NodeJS.Timeout>();
|
|
44
46
|
const lastExecutionTimeRef = useRef<number>(0);
|
|
45
47
|
const pendingQueryRef = useRef<string | null>(null);
|
|
46
48
|
const inflightQueryRef = useRef<string | null>(null);
|
|
47
|
-
const api = useMemo(() => new TractStackAPI(), []);
|
|
49
|
+
const api = useMemo(() => new TractStackAPI(tenantId), []);
|
|
48
50
|
|
|
49
51
|
const performDiscovery = useCallback(
|
|
50
52
|
async (query: string) => {
|
|
@@ -228,33 +228,11 @@ const enableBunny = import.meta.env.PUBLIC_ENABLE_BUNNY === 'true';
|
|
|
228
228
|
);
|
|
229
229
|
const sessionMeta = document.querySelector('meta[name="session-id"]');
|
|
230
230
|
|
|
231
|
-
// Dynamic Backend URL Fix:
|
|
232
|
-
// If we are on a subdomain (sandbox.domain.com), force the API call to match
|
|
233
|
-
// (sandbox.domain.com:10000) so cookies are sent correctly.
|
|
234
|
-
let dynamicBackendUrl = goBackend;
|
|
235
|
-
try {
|
|
236
|
-
const currentHost = window.location.hostname;
|
|
237
|
-
const backendUrlObj = new URL(goBackend);
|
|
238
|
-
|
|
239
|
-
// If the hostnames differ (e.g. sandbox.site.com vs site.com), align them
|
|
240
|
-
if (currentHost !== backendUrlObj.hostname) {
|
|
241
|
-
backendUrlObj.hostname = currentHost;
|
|
242
|
-
dynamicBackendUrl = backendUrlObj.toString().replace(/\/$/, '');
|
|
243
|
-
}
|
|
244
|
-
} catch (e) {
|
|
245
|
-
console.warn(
|
|
246
|
-
'TractStack: Failed to construct dynamic backend URL',
|
|
247
|
-
e
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
231
|
window.TRACTSTACK_CONFIG = {
|
|
252
232
|
configured: true,
|
|
253
|
-
backendUrl:
|
|
233
|
+
backendUrl: goBackend,
|
|
254
234
|
tenantId: tenantId,
|
|
255
235
|
fontBasePath: fontBasePath,
|
|
256
|
-
// Use the meta tag if it exists (for subsequent client-side loads),
|
|
257
|
-
// otherwise fall back to the initial server-rendered value.
|
|
258
236
|
storyfragmentId: storyfragmentMeta
|
|
259
237
|
? storyfragmentMeta.content
|
|
260
238
|
: initialStoryfragmentId,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import type { APIRoute } from '@/types/astro';
|
|
2
2
|
|
|
3
|
-
export const GET: APIRoute = async ({ request }) => {
|
|
3
|
+
export const GET: APIRoute = async ({ request, locals }) => {
|
|
4
4
|
const goBackend =
|
|
5
5
|
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
6
6
|
const tenantId =
|
|
7
|
-
|
|
8
|
-
import.meta.env.PUBLIC_TENANTID ||
|
|
9
|
-
'default';
|
|
7
|
+
locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default';
|
|
10
8
|
|
|
11
9
|
try {
|
|
12
10
|
// Get Authorization header from frontend
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { APIRoute } from '@/types/astro';
|
|
2
2
|
|
|
3
|
-
export const POST: APIRoute = async ({ request }) => {
|
|
3
|
+
export const POST: APIRoute = async ({ request, locals }) => {
|
|
4
|
+
const tenantId =
|
|
5
|
+
locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default';
|
|
6
|
+
|
|
4
7
|
try {
|
|
5
8
|
// Parse request body
|
|
6
9
|
const body = await request.json();
|
|
@@ -17,10 +20,6 @@ export const POST: APIRoute = async ({ request }) => {
|
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
// Get tenant info from environment
|
|
20
|
-
const tenantId =
|
|
21
|
-
request.headers.get('X-Tenant-ID') ||
|
|
22
|
-
import.meta.env.PUBLIC_TENANTID ||
|
|
23
|
-
'default';
|
|
24
23
|
const backendUrl =
|
|
25
24
|
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
26
25
|
|
|
@@ -2,14 +2,29 @@ import type { APIRoute } from '@/types/astro';
|
|
|
2
2
|
|
|
3
3
|
export const POST: APIRoute = async ({ cookies }) => {
|
|
4
4
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
});
|
|
5
|
+
const goBackend =
|
|
6
|
+
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
7
|
+
let rootDomain: string | undefined;
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
try {
|
|
10
|
+
const url = new URL(goBackend);
|
|
11
|
+
// Only set domain for non-localhost to preserve local dev behavior
|
|
12
|
+
if (url.hostname !== 'localhost' && url.hostname !== '127.0.0.1') {
|
|
13
|
+
rootDomain = url.hostname;
|
|
14
|
+
}
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.warn('Logout: Failed to parse backend URL for cookie domain', e);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Determine the options ONCE to prevent overwriting
|
|
20
|
+
const cookieOptions: any = { path: '/' };
|
|
21
|
+
if (rootDomain) {
|
|
22
|
+
cookieOptions.domain = rootDomain;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Execute deletion with the single, correct configuration
|
|
26
|
+
cookies.delete('admin_auth', cookieOptions);
|
|
27
|
+
cookies.delete('editor_auth', cookieOptions);
|
|
13
28
|
|
|
14
29
|
return new Response(
|
|
15
30
|
JSON.stringify({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { APIRoute } from '@/types/astro';
|
|
2
2
|
|
|
3
|
-
export const POST: APIRoute = async ({ request }) => {
|
|
3
|
+
export const POST: APIRoute = async ({ request, locals }) => {
|
|
4
4
|
const GO_BACKEND =
|
|
5
5
|
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
6
6
|
|
|
@@ -11,13 +11,15 @@ export const POST: APIRoute = async ({ request }) => {
|
|
|
11
11
|
// Create abort controller for request timeout
|
|
12
12
|
const controller = new AbortController();
|
|
13
13
|
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout
|
|
14
|
+
const tenantId =
|
|
15
|
+
locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default';
|
|
14
16
|
|
|
15
17
|
try {
|
|
16
18
|
const response = await fetch(`${GO_BACKEND}/api/v1/auth/profile`, {
|
|
17
19
|
method: 'POST',
|
|
18
20
|
headers: {
|
|
19
21
|
'Content-Type': 'application/json',
|
|
20
|
-
'X-Tenant-ID':
|
|
22
|
+
'X-Tenant-ID': tenantId,
|
|
21
23
|
...(request.headers.get('Authorization') && {
|
|
22
24
|
Authorization: request.headers.get('Authorization')!,
|
|
23
25
|
}),
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import type { APIRoute } from '@/types/astro';
|
|
2
2
|
|
|
3
|
-
export const POST: APIRoute = async ({ request }) => {
|
|
3
|
+
export const POST: APIRoute = async ({ request, locals }) => {
|
|
4
|
+
const tenantId =
|
|
5
|
+
locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default';
|
|
4
6
|
const goBackend =
|
|
5
7
|
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
6
8
|
const sharedSecret = import.meta.env.PRIVATE_SANDBOX_SECRET;
|
|
7
|
-
const tenantId =
|
|
8
|
-
request.headers.get('X-Tenant-ID') ||
|
|
9
|
-
import.meta.env.PUBLIC_TENANTID ||
|
|
10
|
-
'default';
|
|
11
9
|
|
|
12
10
|
if (!sharedSecret || sharedSecret === 'false' || sharedSecret === 'true') {
|
|
13
11
|
return new Response(
|
|
@@ -3,18 +3,15 @@ import { createTailwindcss } from '@mhsdesign/jit-browser-tailwindcss';
|
|
|
3
3
|
import fs from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
|
-
export const POST: APIRoute = async ({ request }) => {
|
|
6
|
+
export const POST: APIRoute = async ({ request, locals }) => {
|
|
7
|
+
const tenantId =
|
|
8
|
+
locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default';
|
|
9
|
+
const goBackend =
|
|
10
|
+
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
11
|
+
|
|
7
12
|
try {
|
|
8
13
|
const { dirtyPaneIds, dirtyClasses } = await request.json();
|
|
9
14
|
|
|
10
|
-
const tenantId =
|
|
11
|
-
request.headers.get('X-Tenant-ID') ||
|
|
12
|
-
import.meta.env.PUBLIC_TENANTID ||
|
|
13
|
-
'default';
|
|
14
|
-
|
|
15
|
-
const goBackend =
|
|
16
|
-
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
17
|
-
|
|
18
15
|
// Forward authentication cookies to the backend
|
|
19
16
|
const cookieHeader = request.headers.get('cookie') || '';
|
|
20
17
|
|