astro-tractstack 2.0.0-rc.14 → 2.0.0-rc.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-tractstack",
3
- "version": "2.0.0-rc.14",
3
+ "version": "2.0.0-rc.16",
4
4
  "description": "Astro integration for TractStack - redeeming the web from boring experiences",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -160,12 +160,13 @@ const authStatus = {
160
160
 
161
161
  {/* Edit Icon */}
162
162
  {
163
- isEditable ? (
163
+ isEditable &&
164
+ (authStatus.isAdmin || authStatus.userRole === 'editor') ? (
164
165
  <a
165
166
  data-astro-reload
166
167
  href={!isContext ? `/${slug}/edit` : `/context/${slug}/edit`}
167
168
  class="text-myblue/80 hover:text-myblue hover:rotate-6"
168
- title={!isContext ? 'Edit this story' : 'Edit this pane'}
169
+ title={!isContext ? 'Edit this story' : 'Edit this page'}
169
170
  >
170
171
  <svg
171
172
  class="h-6 w-6"
@@ -51,26 +51,40 @@ const PanelVisibilityWrapper = ({
51
51
  const currentWrapper = wrapperRef.current;
52
52
  if (!currentWrapper) return;
53
53
 
54
- // Always observe the panel, regardless of active state
54
+ // Skip intersection observer for 'add' panels - they behave differently
55
+ if (panelType === 'add') {
56
+ return;
57
+ }
58
+
55
59
  const observer = new IntersectionObserver(
56
60
  (entries) => {
57
- // Only take action if this panel is currently active and not intersecting
58
- if (!entries[0].isIntersecting && isActive) {
59
- nodesCtx.closeAllPanels();
61
+ // Add delay to prevent immediate closing during panel activation
62
+ if (isActive) {
63
+ setTimeout(() => {
64
+ // Double-check the panel is still active before closing
65
+ const currentActiveMode = nodesCtx.activePaneMode.get();
66
+ const stillActive =
67
+ currentActiveMode.panel === panelType &&
68
+ currentActiveMode.paneId === nodeId;
69
+ if (!entries[0].isIntersecting && stillActive) {
70
+ console.log('❌ CLOSING PANEL DUE TO INTERSECTION OBSERVER!', {
71
+ panelType,
72
+ nodeId,
73
+ });
74
+ nodesCtx.closeAllPanels();
75
+ }
76
+ }, 100); // Small delay to allow panel to render
60
77
  }
61
78
  },
62
79
  {
63
- threshold: 0.1, // Close when 90% of the panel is out of view
64
- rootMargin: '-10px', // Small margin to make detection a bit more forgiving
80
+ threshold: 0.1,
81
+ rootMargin: '-10px',
65
82
  }
66
83
  );
67
84
 
68
85
  observer.observe(currentWrapper);
69
-
70
- return () => {
71
- observer.disconnect();
72
- };
73
- }, [nodeId, panelType, nodesCtx, isActive]); // Include isActive in dependencies
86
+ return () => observer.disconnect();
87
+ }, [nodeId, panelType, nodesCtx, isActive]);
74
88
 
75
89
  return (
76
90
  <div
@@ -505,62 +505,56 @@ export default function SaveModal({
505
505
  const { dirtyPaneIds, classes: dirtyClasses } =
506
506
  ctx.getDirtyNodesClassData();
507
507
 
508
- if (dirtyClasses.length === 0) {
509
- addDebugMessage(
510
- 'No dirty classes to process, skipping Tailwind update'
511
- );
512
- } else {
513
- // STEP 1: Generate CSS using Astro API
514
- const astroEndpoint = `/api/tailwind`;
515
- const astroPayload = { dirtyPaneIds, dirtyClasses };
516
- const astroResponse = await fetch(astroEndpoint, {
517
- method: 'POST',
518
- headers: {
519
- 'Content-Type': 'application/json',
520
- 'X-Tenant-ID': tenantId,
521
- },
522
- credentials: 'include',
523
- body: JSON.stringify(astroPayload),
524
- });
525
-
526
- if (!astroResponse.ok) {
527
- throw new Error(
528
- `CSS generation failed! status: ${astroResponse.status}`
529
- );
530
- }
508
+ // STEP 1: Generate CSS using Astro API
509
+ const astroEndpoint = `/api/tailwind`;
510
+ const astroPayload = { dirtyPaneIds, dirtyClasses };
511
+ const astroResponse = await fetch(astroEndpoint, {
512
+ method: 'POST',
513
+ headers: {
514
+ 'Content-Type': 'application/json',
515
+ 'X-Tenant-ID': tenantId,
516
+ },
517
+ credentials: 'include',
518
+ body: JSON.stringify(astroPayload),
519
+ });
531
520
 
532
- const astroResult = await astroResponse.json();
521
+ if (!astroResponse.ok) {
522
+ throw new Error(
523
+ `CSS generation failed! status: ${astroResponse.status}`
524
+ );
525
+ }
533
526
 
534
- if (!astroResult.success || !astroResult.generatedCss) {
535
- throw new Error('CSS generation failed: no CSS returned');
536
- }
527
+ const astroResult = await astroResponse.json();
537
528
 
538
- addDebugMessage(
539
- `CSS generated: ${astroResult.generatedCss.length} bytes for ${dirtyClasses.length} classes`
540
- );
529
+ if (!astroResult.success || !astroResult.generatedCss) {
530
+ throw new Error('CSS generation failed: no CSS returned');
531
+ }
541
532
 
542
- // STEP 2: Save CSS to Go backend
543
- const goEndpoint = `${goBackend}/api/v1/tailwind/update`;
544
- const goPayload = { frontendCss: astroResult.generatedCss };
545
- const goResponse = await fetch(goEndpoint, {
546
- method: 'POST',
547
- headers: {
548
- 'Content-Type': 'application/json',
549
- 'X-Tenant-ID': tenantId,
550
- },
551
- credentials: 'include',
552
- body: JSON.stringify(goPayload),
553
- });
533
+ addDebugMessage(
534
+ `CSS generated: ${astroResult.generatedCss.length} bytes for ${dirtyClasses.length} classes`
535
+ );
554
536
 
555
- if (!goResponse.ok) {
556
- throw new Error(`CSS save failed! status: ${goResponse.status}`);
557
- }
537
+ // STEP 2: Save CSS to Go backend
538
+ const goEndpoint = `${goBackend}/api/v1/tailwind/update`;
539
+ const goPayload = { frontendCss: astroResult.generatedCss };
540
+ const goResponse = await fetch(goEndpoint, {
541
+ method: 'POST',
542
+ headers: {
543
+ 'Content-Type': 'application/json',
544
+ 'X-Tenant-ID': tenantId,
545
+ },
546
+ credentials: 'include',
547
+ body: JSON.stringify(goPayload),
548
+ });
558
549
 
559
- const goResult = await goResponse.json();
560
- addDebugMessage(
561
- `CSS saved successfully: stylesVer ${goResult.stylesVer}`
562
- );
550
+ if (!goResponse.ok) {
551
+ throw new Error(`CSS save failed! status: ${goResponse.status}`);
563
552
  }
553
+
554
+ const goResult = await goResponse.json();
555
+ addDebugMessage(
556
+ `CSS saved successfully: stylesVer ${goResult.stylesVer}`
557
+ );
564
558
  } catch (error) {
565
559
  const errorMsg =
566
560
  error instanceof Error ? error.message : 'Unknown error';
@@ -238,7 +238,7 @@ export default function StoryKeepDashboard_Analytics({
238
238
  )}
239
239
 
240
240
  {/* Stats Cards Grid */}
241
- <div className="mb-6 grid grid-cols-1 gap-4 md:grid-cols-3">
241
+ <div className="mb-6 grid grid-cols-3 gap-4">
242
242
  {stats.map((item) => {
243
243
  const period = item.period;
244
244
  let firstTimeValue = 0,
@@ -261,11 +261,11 @@ export default function StoryKeepDashboard_Analytics({
261
261
  return (
262
262
  <div
263
263
  key={item.period}
264
- className="rounded-lg border border-gray-100 bg-white px-4 py-3 shadow-sm transition-colors hover:border-cyan-100"
264
+ className="rounded-lg border border-gray-100 bg-white px-2 py-2.5 shadow-sm transition-colors hover:border-cyan-100 md:px-4 md:py-3"
265
265
  >
266
266
  <dt className="text-sm font-bold text-gray-800">{item.name}</dt>
267
267
 
268
- <dd className="mt-2">
268
+ <dd className="mt-1 md:mt-2">
269
269
  <div className="flex items-end justify-between">
270
270
  <div className="flex-1">
271
271
  <div className="text-sm text-gray-600">Events</div>
@@ -276,10 +276,11 @@ export default function StoryKeepDashboard_Analytics({
276
276
  </div>
277
277
  </dd>
278
278
 
279
- <hr className="my-3.5 border-gray-100" />
279
+ <hr className="my-1.5 border-gray-100 md:my-3.5" />
280
280
 
281
281
  <dd>
282
- <div className="flex items-end justify-between">
282
+ {/* Desktop: side-by-side layout */}
283
+ <div className="hidden items-end justify-between md:flex">
283
284
  <div className="flex-1">
284
285
  <div className="text-sm text-gray-600">
285
286
  Anonymous Visitors
@@ -299,13 +300,35 @@ export default function StoryKeepDashboard_Analytics({
299
300
  </div>
300
301
  </div>
301
302
  </div>
303
+
304
+ {/* Mobile: stacked layout */}
305
+ <div className="md:hidden">
306
+ <div className="mb-1.5">
307
+ <div className="text-sm text-gray-600">
308
+ Anonymous Visitors
309
+ </div>
310
+ <div className="text-2xl font-bold tracking-tight text-cyan-700">
311
+ {firstTimeValue === 0
312
+ ? '-'
313
+ : formatNumber(firstTimeValue)}
314
+ </div>
315
+ </div>
316
+ <div>
317
+ <div className="text-sm text-gray-600">Known Leads</div>
318
+ <div className="text-2xl font-bold tracking-tight text-cyan-700">
319
+ {returningValue === 0
320
+ ? '-'
321
+ : formatNumber(returningValue)}
322
+ </div>
323
+ </div>
324
+ </div>
302
325
  </dd>
303
326
  </div>
304
327
  );
305
328
  })}
306
329
 
307
330
  {/* Total Leads Card */}
308
- <div className="rounded-lg border border-gray-100 bg-white px-4 py-3 shadow-sm transition-colors hover:border-cyan-100 md:col-span-3">
331
+ <div className="col-span-3 rounded-lg border border-gray-100 bg-white px-4 py-3 shadow-sm transition-colors hover:border-cyan-100">
309
332
  <div className="flex items-center justify-between">
310
333
  <dt className="text-sm font-bold text-gray-800">Total Leads</dt>
311
334
  <div className="flex items-center gap-2">
@@ -34,5 +34,42 @@ const mainStylesUrl = isDev
34
34
  <div class="max-w-5xl p-3.5 md:p-8">
35
35
  <RegistrationForm client:load isInitMode={true} />
36
36
  </div>
37
+
38
+ <script>
39
+ // Ensure clean slate for fresh installation
40
+ (function initCleanSlate() {
41
+ try {
42
+ // Clear admin/editor auth cookies
43
+ document.cookie =
44
+ 'admin_auth=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
45
+ document.cookie =
46
+ 'editor_auth=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
47
+
48
+ // Clear all TractStack localStorage items
49
+ const tractStackKeys = [];
50
+ for (let i = 0; i < localStorage.length; i++) {
51
+ const key = localStorage.key(i);
52
+ if (key && key.startsWith('tractstack_')) {
53
+ tractStackKeys.push(key);
54
+ }
55
+ }
56
+ tractStackKeys.forEach((key) => localStorage.removeItem(key));
57
+
58
+ // Clear session-related items
59
+ localStorage.removeItem('tractstack_session_id');
60
+ localStorage.removeItem('tractstack_fingerprint');
61
+ localStorage.removeItem('tractstack_visit');
62
+ localStorage.removeItem('tractstack_entered_tracked');
63
+
64
+ // Clear session cookie
65
+ document.cookie =
66
+ 'tractstack_session_id=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
67
+
68
+ console.log('TractStack: Clean slate initialization complete');
69
+ } catch (error) {
70
+ console.warn('TractStack: Error during clean slate init:', error);
71
+ }
72
+ })();
73
+ </script>
37
74
  </body>
38
75
  </html>
@@ -113,9 +113,9 @@ export function setupLayoutObservers(): void {
113
113
  toolModeNav.style.left = '0';
114
114
 
115
115
  // Add margin to main content when nav becomes fixed (nav no longer takes flex space)
116
- if (mainContent) {
117
- mainContent.classList.add('md:ml-16');
118
- }
116
+ //if (mainContent) {
117
+ // mainContent.classList.add('md:ml-16');
118
+ //}
119
119
  } else {
120
120
  // Normal static positioning when header is visible
121
121
  toolModeNav.classList.remove('md:fixed');
@@ -124,9 +124,9 @@ export function setupLayoutObservers(): void {
124
124
  toolModeNav.style.left = '';
125
125
 
126
126
  // Remove margin from main content when nav is static (nav takes flex space naturally)
127
- if (mainContent) {
128
- mainContent.classList.remove('md:ml-16');
129
- }
127
+ //if (mainContent) {
128
+ // mainContent.classList.remove('md:ml-16');
129
+ //}
130
130
  }
131
131
  }
132
132
  };