astro-tractstack 2.2.10 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +1 -1
  2. package/bin/create-tractstack.js +2 -2
  3. package/dist/index.js +177 -18
  4. package/package.json +4 -2
  5. package/templates/custom/minimal/CodeHook.astro +22 -5
  6. package/templates/custom/shopify/Cart.tsx +372 -0
  7. package/templates/custom/shopify/CartIcon.tsx +47 -0
  8. package/templates/custom/shopify/CartModal.tsx +63 -0
  9. package/templates/custom/shopify/CheckoutModal.tsx +576 -0
  10. package/templates/custom/shopify/NativeBookingCalendar.tsx +375 -0
  11. package/templates/custom/shopify/ShopifyCartManager.tsx +200 -0
  12. package/templates/custom/shopify/ShopifyCheckout.tsx +167 -0
  13. package/templates/custom/shopify/ShopifyProductGrid.tsx +247 -0
  14. package/templates/custom/shopify/ShopifyServiceList.tsx +135 -0
  15. package/templates/custom/shopify/cart.astro +23 -0
  16. package/templates/custom/with-examples/CodeHook.astro +17 -1
  17. package/templates/custom/with-examples/ProductGrid.astro +1 -1
  18. package/templates/src/client/app.js +4 -2
  19. package/templates/src/components/Footer.astro +4 -4
  20. package/templates/src/components/Header.astro +44 -12
  21. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +3 -3
  22. package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +2 -2
  23. package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +2 -2
  24. package/templates/src/components/edit/pane/steps/AiLibraryCopyStep.tsx +3 -3
  25. package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +2 -2
  26. package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +7 -7
  27. package/templates/src/components/form/advanced/APIConfigSection.tsx +407 -38
  28. package/templates/src/components/form/shopify/SchedulingSection.tsx +354 -0
  29. package/templates/src/components/storykeep/Dashboard.tsx +18 -4
  30. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +1 -0
  31. package/templates/src/components/storykeep/Dashboard_Content.tsx +5 -96
  32. package/templates/src/components/storykeep/Dashboard_Shopify.tsx +668 -0
  33. package/templates/src/components/storykeep/StoryKeepBackdrop.astro +43 -23
  34. package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +14 -5
  35. package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +0 -14
  36. package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +36 -13
  37. package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +5 -2
  38. package/templates/src/components/storykeep/controls/content/ManageContent.tsx +4 -11
  39. package/templates/src/components/storykeep/controls/content/MenuTable.tsx +14 -5
  40. package/templates/src/components/storykeep/controls/content/ProductTable.tsx +333 -0
  41. package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +9 -5
  42. package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +108 -8
  43. package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +13 -4
  44. package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +14 -5
  45. package/templates/src/components/storykeep/shopify/ShopifyDashboard.tsx +111 -0
  46. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx +393 -0
  47. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Products.tsx +46 -0
  48. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Schedule.tsx +78 -0
  49. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Search.tsx +55 -0
  50. package/templates/src/components/storykeep/shopify/ShopifyDashboard_Services.tsx +47 -0
  51. package/templates/src/lib/resources.ts +11 -21
  52. package/templates/src/pages/api/auth/lookup-lead.ts +72 -0
  53. package/templates/src/pages/api/booking/availability.ts +72 -0
  54. package/templates/src/pages/api/booking/cancel.ts +73 -0
  55. package/templates/src/pages/api/booking/confirm.ts +82 -0
  56. package/templates/src/pages/api/booking/hold.ts +75 -0
  57. package/templates/src/pages/api/booking/list.ts +66 -0
  58. package/templates/src/pages/api/booking/metrics.ts +60 -0
  59. package/templates/src/pages/api/booking/release.ts +76 -0
  60. package/templates/src/pages/api/sandbox.ts +2 -2
  61. package/templates/src/pages/api/shopify/createCart.ts +69 -0
  62. package/templates/src/pages/api/shopify/getProducts.ts +64 -0
  63. package/templates/src/pages/storykeep/login.astro +26 -24
  64. package/templates/src/pages/storykeep/logout.astro +1 -10
  65. package/templates/src/pages/storykeep/manage.astro +69 -0
  66. package/templates/src/pages/storykeep/{content.astro → pages.astro} +4 -8
  67. package/templates/src/pages/storykeep/shopify.astro +101 -0
  68. package/templates/src/stores/navigation.ts +3 -42
  69. package/templates/src/stores/nodes.ts +3 -1
  70. package/templates/src/stores/resources.ts +7 -10
  71. package/templates/src/stores/shopify.ts +266 -0
  72. package/templates/src/types/tractstack.ts +75 -0
  73. package/templates/src/utils/api/advancedConfig.ts +7 -1
  74. package/templates/src/utils/api/advancedHelpers.ts +87 -7
  75. package/templates/src/utils/api/bookingHelpers.ts +125 -0
  76. package/templates/src/utils/api/brandHelpers.ts +14 -0
  77. package/templates/src/utils/api/resourceConfig.ts +13 -5
  78. package/templates/src/utils/auth.ts +29 -9
  79. package/templates/src/utils/compositor/aiGeneration.ts +3 -3
  80. package/templates/src/utils/compositor/aiPaneParser.ts +2 -2
  81. package/templates/src/utils/customHelpers.ts +49 -0
  82. package/templates/src/utils/helpers.ts +59 -0
  83. package/templates/src/utils/profileStorage.ts +5 -0
  84. package/templates/src/utils/tenantResolver.ts +2 -1
  85. package/utils/inject-files.ts +161 -2
@@ -0,0 +1,354 @@
1
+ import { useState } from 'react';
2
+ import type { BrandConfigState, TimeBlock } from '@/types/tractstack';
3
+ import type { FormStateReturn } from '@/hooks/useFormState';
4
+
5
+ interface SchedulingSectionProps {
6
+ formState: FormStateReturn<BrandConfigState>;
7
+ }
8
+
9
+ const DAYS = [
10
+ 'monday',
11
+ 'tuesday',
12
+ 'wednesday',
13
+ 'thursday',
14
+ 'friday',
15
+ 'saturday',
16
+ 'sunday',
17
+ ];
18
+
19
+ const TIMEZONE_OPTIONS = [
20
+ {
21
+ group: 'North America (Canada & US)',
22
+ zones: [
23
+ { label: 'Pacific Time (Vancouver / BC)', value: 'America/Vancouver' },
24
+ {
25
+ label: 'Pacific Time (Los Angeles / Seattle)',
26
+ value: 'America/Los_Angeles',
27
+ },
28
+ {
29
+ label: 'Mountain Time (Edmonton / Calgary)',
30
+ value: 'America/Edmonton',
31
+ },
32
+ { label: 'Mountain Time (Denver / Salt Lake)', value: 'America/Denver' },
33
+ { label: 'Mountain Time - No DST (Phoenix)', value: 'America/Phoenix' },
34
+ { label: 'Central Time (Winnipeg)', value: 'America/Winnipeg' },
35
+ { label: 'Central Time (Chicago / Dallas)', value: 'America/Chicago' },
36
+ { label: 'Eastern Time (Toronto / Montreal)', value: 'America/Toronto' },
37
+ { label: 'Eastern Time (New York / Miami)', value: 'America/New_York' },
38
+ { label: 'Atlantic Time (Halifax)', value: 'America/Halifax' },
39
+ { label: "Newfoundland Time (St. John's)", value: 'America/St_Johns' },
40
+ { label: 'Alaska Time (Anchorage)', value: 'America/Anchorage' },
41
+ { label: 'Hawaii Time (Honolulu)', value: 'Pacific/Honolulu' },
42
+ ],
43
+ },
44
+ {
45
+ group: 'Latin America',
46
+ zones: [
47
+ { label: 'Mexico City', value: 'America/Mexico_City' },
48
+ { label: 'Bogota / Lima', value: 'America/Bogota' },
49
+ { label: 'São Paulo', value: 'America/Sao_Paulo' },
50
+ ],
51
+ },
52
+ {
53
+ group: 'Europe & Africa',
54
+ zones: [
55
+ { label: 'London / Dublin', value: 'Europe/London' },
56
+ { label: 'Paris / Berlin / Rome', value: 'Europe/Paris' },
57
+ { label: 'Athens / Cairo', value: 'Europe/Athens' },
58
+ { label: 'Lagos', value: 'Africa/Lagos' },
59
+ { label: 'Johannesburg', value: 'Africa/Johannesburg' },
60
+ ],
61
+ },
62
+ {
63
+ group: 'Asia & Oceania',
64
+ zones: [
65
+ { label: 'Dubai', value: 'Asia/Dubai' },
66
+ { label: 'Singapore / HK', value: 'Asia/Singapore' },
67
+ { label: 'Tokyo', value: 'Asia/Tokyo' },
68
+ { label: 'Sydney / Melbourne', value: 'Australia/Sydney' },
69
+ { label: 'Auckland', value: 'Pacific/Auckland' },
70
+ ],
71
+ },
72
+ { label: 'UTC / GMT', value: 'UTC' },
73
+ ];
74
+
75
+ function getUtcFromWallTime(wallTimeIso: string, timeZone: string): string {
76
+ const [datePart, timePart] = wallTimeIso.split('T');
77
+ const [year, month, day] = datePart.split('-').map(Number);
78
+ const [hour, minute] = timePart.split(':').map(Number);
79
+
80
+ const pad = (n: number) => n.toString().padStart(2, '0');
81
+ const pseudoUtc = new Date(
82
+ `${year}-${pad(month)}-${pad(day)}T${pad(hour)}:${pad(minute)}:00Z`
83
+ );
84
+
85
+ const formatter = new Intl.DateTimeFormat('en-US', {
86
+ timeZone,
87
+ year: 'numeric',
88
+ month: '2-digit',
89
+ day: '2-digit',
90
+ hour: '2-digit',
91
+ minute: '2-digit',
92
+ hour12: false,
93
+ });
94
+
95
+ const parts = formatter.formatToParts(pseudoUtc);
96
+ const getPart = (type: string) => parts.find((p) => p.type === type)?.value;
97
+
98
+ const tzDate = new Date(
99
+ `${getPart('year')}-${getPart('month')}-${getPart('day')}T${getPart('hour')}:${getPart('minute')}:00Z`
100
+ );
101
+ const diff = pseudoUtc.getTime() - tzDate.getTime();
102
+
103
+ return new Date(pseudoUtc.getTime() + diff).toISOString();
104
+ }
105
+
106
+ export default function SchedulingSection({
107
+ formState,
108
+ }: SchedulingSectionProps) {
109
+ const { state, updateField } = formState;
110
+ const config = state.scheduling;
111
+ const [newBlock, setNewBlock] = useState<TimeBlock>({ start: '', end: '' });
112
+
113
+ const updateHours = (day: string, field: keyof TimeBlock, value: string) => {
114
+ const updatedHours = { ...config.businessHours };
115
+ if (!updatedHours[day]) {
116
+ updatedHours[day] = { start: '09:00', end: '17:00' };
117
+ }
118
+ updatedHours[day] = { ...updatedHours[day], [field]: value };
119
+ updateField('scheduling', { ...config, businessHours: updatedHours });
120
+ };
121
+
122
+ const toggleDay = (day: string) => {
123
+ const updatedHours = { ...config.businessHours };
124
+ if (updatedHours[day]) {
125
+ delete updatedHours[day];
126
+ } else {
127
+ updatedHours[day] = { start: '09:00', end: '17:00' };
128
+ }
129
+ updateField('scheduling', { ...config, businessHours: updatedHours });
130
+ };
131
+
132
+ const addUnavailableBlock = () => {
133
+ if (!newBlock.start || !newBlock.end) return;
134
+
135
+ const startUtc = getUtcFromWallTime(newBlock.start, config.timezone);
136
+ const endUtc = getUtcFromWallTime(newBlock.end, config.timezone);
137
+
138
+ const updated = [
139
+ ...config.unavailableHours,
140
+ { start: startUtc, end: endUtc },
141
+ ];
142
+ updateField('scheduling', { ...config, unavailableHours: updated });
143
+ setNewBlock({ start: '', end: '' });
144
+ };
145
+
146
+ const removeUnavailableBlock = (index: number) => {
147
+ const updated = config.unavailableHours.filter((_, i) => i !== index);
148
+ updateField('scheduling', { ...config, unavailableHours: updated });
149
+ };
150
+
151
+ return (
152
+ <div className="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
153
+ <h3 className="mb-6 text-xl font-extrabold text-gray-900">
154
+ Shopify Store
155
+ </h3>
156
+ <div className="space-y-10 divide-y divide-gray-100">
157
+ <div className="pt-2">
158
+ <div className="grid grid-cols-1 gap-x-6 gap-y-8 md:grid-cols-6 xl:grid-cols-12">
159
+ <div className="md:col-span-3 xl:col-span-4">
160
+ <label className="block text-xs font-black uppercase tracking-widest text-gray-500">
161
+ Store Timezone
162
+ </label>
163
+ <select
164
+ value={config.timezone}
165
+ onChange={(e) =>
166
+ updateField('scheduling', {
167
+ ...config,
168
+ timezone: e.target.value,
169
+ })
170
+ }
171
+ className="mt-2 block w-full rounded-md border-gray-300 bg-white px-2 py-3 focus:border-cyan-700 focus:ring-cyan-700 md:text-sm"
172
+ >
173
+ {TIMEZONE_OPTIONS.map((opt) =>
174
+ opt.group ? (
175
+ <optgroup key={opt.group} label={opt.group}>
176
+ {opt.zones.map((z) => (
177
+ <option key={z.value} value={z.value}>
178
+ {z.label}
179
+ </option>
180
+ ))}
181
+ </optgroup>
182
+ ) : (
183
+ <option key={opt.value} value={opt.value}>
184
+ {opt.label}
185
+ </option>
186
+ )
187
+ )}
188
+ </select>
189
+ <p className="mt-1 text-xs font-bold uppercase text-gray-400">
190
+ Stored: {config.timezone}
191
+ </p>
192
+ </div>
193
+ <div className="md:col-span-3 xl:col-span-4">
194
+ <label className="block text-xs font-black uppercase tracking-widest text-gray-500">
195
+ Buffer Gap (Minutes)
196
+ </label>
197
+ <input
198
+ type="number"
199
+ value={config.bufferGapsMinutes}
200
+ onChange={(e) =>
201
+ updateField('scheduling', {
202
+ ...config,
203
+ bufferGapsMinutes: parseInt(e.target.value) || 0,
204
+ })
205
+ }
206
+ className="mt-2 block w-full rounded-md border-gray-300 px-2 py-3 focus:border-cyan-700 focus:ring-cyan-700 md:text-sm"
207
+ />
208
+ </div>
209
+ <div className="md:col-span-3 xl:col-span-4">
210
+ <label className="block text-xs font-black uppercase tracking-widest text-gray-500">
211
+ Max Duration (Minutes)
212
+ </label>
213
+ <input
214
+ type="number"
215
+ value={config.maxLengthMinutes}
216
+ onChange={(e) =>
217
+ updateField('scheduling', {
218
+ ...config,
219
+ maxLengthMinutes: parseInt(e.target.value) || 0,
220
+ })
221
+ }
222
+ className="mt-2 block w-full rounded-md border-gray-300 px-2 py-3 focus:border-cyan-700 focus:ring-cyan-700 md:text-sm"
223
+ />
224
+ </div>
225
+ </div>
226
+ </div>
227
+
228
+ <div className="pt-10">
229
+ <h4 className="text-sm font-black uppercase tracking-widest text-gray-400">
230
+ Weekly Business Hours
231
+ </h4>
232
+ <div className="mt-6 space-y-6">
233
+ {DAYS.map((day) => (
234
+ <div
235
+ key={day}
236
+ className="flex flex-wrap items-center gap-4 md:flex-nowrap"
237
+ >
238
+ <div className="w-24 text-sm font-bold capitalize text-gray-900 md:w-32">
239
+ {day}
240
+ </div>
241
+ <button
242
+ type="button"
243
+ onClick={() => toggleDay(day)}
244
+ className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-cyan-700 focus:ring-offset-2 ${config.businessHours[day] ? 'bg-cyan-600' : 'bg-gray-200'}`}
245
+ >
246
+ <span
247
+ className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${config.businessHours[day] ? 'translate-x-5' : 'translate-x-0'}`}
248
+ />
249
+ </button>
250
+ {config.businessHours[day] && (
251
+ <div className="flex items-center space-x-3">
252
+ <input
253
+ type="time"
254
+ value={config.businessHours[day].start}
255
+ onChange={(e) =>
256
+ updateHours(day, 'start', e.target.value)
257
+ }
258
+ className="rounded-md border-gray-300 px-2 shadow-sm focus:border-cyan-700 focus:ring-cyan-700 md:text-sm"
259
+ />
260
+ <span className="text-xs font-bold text-gray-400">TO</span>
261
+ <input
262
+ type="time"
263
+ value={config.businessHours[day].end}
264
+ onChange={(e) => updateHours(day, 'end', e.target.value)}
265
+ className="rounded-md border-gray-300 px-2 shadow-sm focus:border-cyan-700 focus:ring-cyan-700 md:text-sm"
266
+ />
267
+ </div>
268
+ )}
269
+ </div>
270
+ ))}
271
+ </div>
272
+ </div>
273
+
274
+ <div className="pt-10">
275
+ <h4 className="text-sm font-black uppercase tracking-widest text-gray-400">
276
+ Blackout Dates
277
+ </h4>
278
+ <div className="mt-6 space-y-6">
279
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-3 xl:grid-cols-4">
280
+ <input
281
+ type="datetime-local"
282
+ value={newBlock.start}
283
+ onChange={(e) =>
284
+ setNewBlock({ ...newBlock, start: e.target.value })
285
+ }
286
+ className="rounded-md border-gray-300 px-2 py-3 focus:border-cyan-700 focus:ring-cyan-700 md:text-sm"
287
+ />
288
+ <input
289
+ type="datetime-local"
290
+ value={newBlock.end}
291
+ onChange={(e) =>
292
+ setNewBlock({ ...newBlock, end: e.target.value })
293
+ }
294
+ className="rounded-md border-gray-300 px-2 py-3 focus:border-cyan-700 focus:ring-cyan-700 md:text-sm"
295
+ />
296
+ <button
297
+ type="button"
298
+ onClick={addUnavailableBlock}
299
+ className="inline-flex justify-center rounded-md border border-transparent bg-cyan-600 px-6 py-3 text-sm font-black uppercase tracking-widest text-white shadow-sm hover:bg-cyan-500 md:col-span-1"
300
+ >
301
+ Add Blackout
302
+ </button>
303
+ </div>
304
+ <div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
305
+ {config.unavailableHours.map((block: TimeBlock, idx: number) => (
306
+ <div
307
+ key={idx}
308
+ className="flex items-center justify-between rounded-lg border border-gray-100 bg-gray-50 p-4 shadow-sm"
309
+ >
310
+ <div className="flex flex-col">
311
+ <span className="text-xs font-black uppercase text-gray-400">
312
+ Closed Period
313
+ </span>
314
+ <span className="text-xs font-bold text-gray-700">
315
+ {new Date(block.start).toLocaleString('en-US', {
316
+ dateStyle: 'short',
317
+ timeStyle: 'short',
318
+ timeZone: config.timezone,
319
+ })}{' '}
320
+ —{' '}
321
+ {new Date(block.end).toLocaleString('en-US', {
322
+ dateStyle: 'short',
323
+ timeStyle: 'short',
324
+ timeZone: config.timezone,
325
+ })}
326
+ </span>
327
+ </div>
328
+ <button
329
+ onClick={() => removeUnavailableBlock(idx)}
330
+ className="ml-4 rounded-full p-1 text-gray-400 transition-colors hover:bg-red-50 hover:text-red-600"
331
+ >
332
+ <svg
333
+ className="h-5 w-5"
334
+ fill="none"
335
+ viewBox="0 0 24 24"
336
+ stroke="currentColor"
337
+ >
338
+ <path
339
+ strokeLinecap="round"
340
+ strokeLinejoin="round"
341
+ strokeWidth={2}
342
+ d="M6 18L18 6M6 6l12 12"
343
+ />
344
+ </svg>
345
+ </button>
346
+ </div>
347
+ ))}
348
+ </div>
349
+ </div>
350
+ </div>
351
+ </div>
352
+ </div>
353
+ );
354
+ }
@@ -52,9 +52,14 @@ export default function StoryKeepDashboard({
52
52
  current: shouldShowInactiveTabs ? false : activeTab === 'analytics',
53
53
  },
54
54
  {
55
- id: 'content',
56
- name: 'Content',
57
- current: activeTab === 'content',
55
+ id: 'pages',
56
+ name: 'Web Pages',
57
+ current: activeTab === 'pages',
58
+ },
59
+ {
60
+ id: 'manage',
61
+ name: 'Manage Content',
62
+ current: activeTab === 'manage',
58
63
  },
59
64
  {
60
65
  id: 'branding',
@@ -70,6 +75,15 @@ export default function StoryKeepDashboard({
70
75
  },
71
76
  ]
72
77
  : []),
78
+ ...(currentBrandConfig?.HAS_SHOPIFY
79
+ ? [
80
+ {
81
+ id: 'shopify',
82
+ name: 'Shopify',
83
+ current: activeTab === 'shopify',
84
+ },
85
+ ]
86
+ : []),
73
87
  ];
74
88
 
75
89
  useEffect(() => {
@@ -135,7 +149,7 @@ export default function StoryKeepDashboard({
135
149
 
136
150
  {/* Tab Navigation */}
137
151
  <div className="border-b border-gray-200">
138
- <nav className="-mb-px flex gap-x-8" aria-label="Tabs">
152
+ <nav className="-mb-px flex flex-wrap gap-x-8" aria-label="Tabs">
139
153
  {tabs.map((tab) => (
140
154
  <a
141
155
  key={tab.id}
@@ -72,6 +72,7 @@ export default function StoryKeepDashboard_Advanced({
72
72
  const newState = convertToLocalState(newStatus);
73
73
  formState.resetToState(newState);
74
74
 
75
+ window.location.reload();
75
76
  return newState;
76
77
  },
77
78
  });
@@ -1,39 +1,17 @@
1
1
  import { useState, useEffect } from 'react';
2
- import { classNames } from '@/utils/helpers';
3
2
  import { TractStackAPI } from '@/utils/api';
4
- import {
5
- handleContentSubtabChange,
6
- restoreTabNavigation,
7
- } from '@/stores/navigation';
8
3
  import ContentBrowser from './controls/content/ContentBrowser';
9
- import ManageContent from './controls/content/ManageContent';
10
4
  import type { FullContentMapItem } from '@/types/tractstack';
11
5
 
12
6
  interface StoryKeepDashboardContentProps {
13
7
  fullContentMap: FullContentMapItem[];
14
8
  homeSlug: string;
15
- createMenu: boolean;
16
9
  }
17
10
 
18
- interface ContentTab {
19
- id: string;
20
- name: string;
21
- }
22
-
23
- const contentTabs: ContentTab[] = [
24
- { id: 'webpages', name: 'Web Pages' },
25
- { id: 'manage', name: 'Manage Content' },
26
- ];
27
-
28
11
  const StoryKeepDashboard_Content = ({
29
12
  fullContentMap,
30
13
  homeSlug,
31
- createMenu,
32
14
  }: StoryKeepDashboardContentProps) => {
33
- const [activeContentTab, setActiveContentTab] = useState('webpages');
34
- const [navigationRestored, setNavigationRestored] = useState(false);
35
-
36
- // Lightweight analytics data state - only for hotContent
37
15
  const [analytics, setAnalytics] = useState<{
38
16
  dashboard: {
39
17
  hotContent?: Array<{ id: string; totalEvents: number }>;
@@ -48,29 +26,6 @@ const StoryKeepDashboard_Content = ({
48
26
  error: null,
49
27
  });
50
28
 
51
- // Restore navigation state when component mounts or when returning to Content tab
52
- useEffect(() => {
53
- if (!navigationRestored) {
54
- const contentNavigation = restoreTabNavigation();
55
- if (contentNavigation) {
56
- setActiveContentTab(contentNavigation.subtab);
57
- }
58
- setNavigationRestored(true);
59
- }
60
- }, [navigationRestored]);
61
-
62
- // Enhanced content tab change with navigation tracking
63
- const handleContentTabChange = (tabId: string) => {
64
- handleContentSubtabChange(tabId as any, setActiveContentTab);
65
- };
66
-
67
- useEffect(() => {
68
- if (createMenu) {
69
- setActiveContentTab('manage');
70
- }
71
- }, [createMenu]);
72
-
73
- // Lightweight content summary fetch with retry logic
74
29
  useEffect(() => {
75
30
  let retryCount = 0;
76
31
  const maxRetries = 2;
@@ -79,7 +34,6 @@ const StoryKeepDashboard_Content = ({
79
34
  try {
80
35
  setAnalytics((prev) => ({ ...prev, isLoading: true, error: null }));
81
36
 
82
- // Use TractStackAPI like FetchAnalytics does
83
37
  const api = new TractStackAPI(
84
38
  window.TRACTSTACK_CONFIG?.tenantId || 'default'
85
39
  );
@@ -91,8 +45,6 @@ const StoryKeepDashboard_Content = ({
91
45
  }
92
46
 
93
47
  const data = response.data;
94
-
95
- // Check if we got actual data
96
48
  const hasData = data.hotContent && data.hotContent.length > 0;
97
49
 
98
50
  setAnalytics({
@@ -140,56 +92,13 @@ const StoryKeepDashboard_Content = ({
140
92
  fetchContentSummary();
141
93
  }, []);
142
94
 
143
- const renderContentTabContent = () => {
144
- switch (activeContentTab) {
145
- case 'webpages':
146
- return (
147
- <ContentBrowser
148
- analytics={analytics}
149
- fullContentMap={fullContentMap}
150
- homeSlug={homeSlug}
151
- />
152
- );
153
- case 'manage':
154
- return (
155
- <ManageContent
156
- fullContentMap={fullContentMap}
157
- homeSlug={homeSlug}
158
- createMenu={createMenu}
159
- />
160
- );
161
- default:
162
- return null;
163
- }
164
- };
165
-
166
95
  return (
167
96
  <div className="w-full">
168
- {/* Content Sub-Navigation */}
169
- <div className="mb-6">
170
- <div className="border-b border-gray-200">
171
- <nav className="-mb-px flex gap-x-6" aria-label="Content tabs">
172
- {contentTabs.map((tab) => (
173
- <button
174
- key={tab.id}
175
- onClick={() => handleContentTabChange(tab.id)}
176
- className={classNames(
177
- activeContentTab === tab.id
178
- ? 'border-cyan-500 text-cyan-600'
179
- : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700',
180
- 'whitespace-nowrap border-b-2 px-1 py-3 text-sm font-bold'
181
- )}
182
- aria-current={activeContentTab === tab.id ? 'page' : undefined}
183
- >
184
- {tab.name}
185
- </button>
186
- ))}
187
- </nav>
188
- </div>
189
- </div>
190
-
191
- {/* Content Tab Content */}
192
- {renderContentTabContent()}
97
+ <ContentBrowser
98
+ analytics={analytics}
99
+ fullContentMap={fullContentMap}
100
+ homeSlug={homeSlug}
101
+ />
193
102
  </div>
194
103
  );
195
104
  };