astro-tractstack 2.3.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/create-tractstack.js +2 -2
- package/dist/index.js +94 -16
- package/package.json +2 -2
- package/templates/custom/minimal/CodeHook.astro +10 -2
- package/templates/custom/shopify/Cart.tsx +100 -73
- package/templates/custom/shopify/CheckoutModal.tsx +509 -120
- package/templates/custom/shopify/NativeBookingCalendar.tsx +375 -0
- package/templates/custom/shopify/ShopifyCartManager.tsx +92 -37
- package/templates/custom/shopify/ShopifyProductGrid.tsx +139 -173
- package/templates/custom/shopify/ShopifyServiceList.tsx +20 -3
- package/templates/custom/with-examples/CodeHook.astro +10 -2
- package/templates/src/components/Footer.astro +4 -4
- package/templates/src/components/Header.astro +9 -3
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +3 -3
- package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +2 -2
- package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +2 -2
- package/templates/src/components/edit/pane/steps/AiLibraryCopyStep.tsx +3 -3
- package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +2 -2
- package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +7 -7
- package/templates/src/components/form/advanced/APIConfigSection.tsx +244 -2
- package/templates/src/components/form/shopify/SchedulingSection.tsx +354 -0
- package/templates/src/components/storykeep/Dashboard.tsx +1 -1
- package/templates/src/components/storykeep/Dashboard_Shopify.tsx +253 -110
- package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +14 -5
- package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +5 -2
- package/templates/src/components/storykeep/controls/content/MenuTable.tsx +14 -5
- package/templates/src/components/storykeep/controls/content/ProductTable.tsx +180 -101
- package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +9 -5
- package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +13 -4
- package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +14 -5
- package/templates/src/components/storykeep/shopify/ShopifyDashboard.tsx +111 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx +393 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Products.tsx +46 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Schedule.tsx +78 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Search.tsx +55 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Services.tsx +47 -0
- package/templates/src/pages/api/auth/lookup-lead.ts +72 -0
- package/templates/src/pages/api/booking/availability.ts +72 -0
- package/templates/src/pages/api/booking/cancel.ts +73 -0
- package/templates/src/pages/api/booking/confirm.ts +82 -0
- package/templates/src/pages/api/booking/hold.ts +75 -0
- package/templates/src/pages/api/booking/list.ts +66 -0
- package/templates/src/pages/api/booking/metrics.ts +60 -0
- package/templates/src/pages/api/booking/release.ts +76 -0
- package/templates/src/pages/api/sandbox.ts +2 -2
- package/templates/src/pages/api/shopify/createCart.ts +4 -8
- package/templates/src/pages/api/shopify/getProducts.ts +15 -15
- package/templates/src/pages/storykeep/login.astro +21 -14
- package/templates/src/stores/shopify.ts +81 -25
- package/templates/src/types/tractstack.ts +54 -0
- package/templates/src/utils/api/advancedConfig.ts +2 -0
- package/templates/src/utils/api/advancedHelpers.ts +40 -3
- package/templates/src/utils/api/bookingHelpers.ts +125 -0
- package/templates/src/utils/api/brandHelpers.ts +10 -0
- package/templates/src/utils/auth.ts +29 -9
- package/templates/src/utils/compositor/aiGeneration.ts +3 -3
- package/templates/src/utils/compositor/aiPaneParser.ts +2 -2
- package/templates/src/utils/customHelpers.ts +0 -21
- package/templates/src/utils/profileStorage.ts +5 -0
- package/templates/src/utils/tenantResolver.ts +2 -1
- package/utils/inject-files.ts +82 -4
- package/templates/custom/shopify/CalDotComBooking.tsx +0 -44
|
@@ -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
|
+
}
|
|
@@ -149,7 +149,7 @@ export default function StoryKeepDashboard({
|
|
|
149
149
|
|
|
150
150
|
{/* Tab Navigation */}
|
|
151
151
|
<div className="border-b border-gray-200">
|
|
152
|
-
<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">
|
|
153
153
|
{tabs.map((tab) => (
|
|
154
154
|
<a
|
|
155
155
|
key={tab.id}
|