astro-tractstack 2.3.0 → 2.3.2
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 +130 -19
- package/package.json +2 -2
- package/templates/custom/minimal/CodeHook.astro +10 -2
- package/templates/custom/shopify/Cart.tsx +115 -77
- package/templates/custom/shopify/CheckoutModal.tsx +509 -120
- package/templates/custom/shopify/NativeBookingCalendar.tsx +375 -0
- package/templates/custom/shopify/ShopifyCartManager.tsx +91 -45
- package/templates/custom/shopify/ShopifyCheckout.tsx +4 -33
- package/templates/custom/shopify/ShopifyProductGrid.tsx +170 -176
- package/templates/custom/shopify/ShopifyServiceList.tsx +112 -51
- package/templates/custom/with-examples/CodeHook.astro +10 -2
- package/templates/src/components/Footer.astro +6 -6
- package/templates/src/components/Header.astro +23 -11
- package/templates/src/components/Menu.tsx +157 -135
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +2 -2
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +27 -6
- package/templates/src/components/codehooks/EpinetTableView.tsx +153 -112
- package/templates/src/components/codehooks/EpinetWrapper.tsx +4 -1
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +8 -1
- package/templates/src/components/codehooks/ProductCardSetup.tsx +9 -1
- package/templates/src/components/codehooks/ProductGridSetup.tsx +9 -1
- package/templates/src/components/compositor/nodes/BgPaneWrapper.tsx +2 -1
- package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +1 -1
- package/templates/src/components/edit/ToolBar.tsx +2 -1
- package/templates/src/components/edit/context/ContextPaneConfig_slug.tsx +2 -2
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +13 -0
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +3 -3
- package/templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx +2 -2
- package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +2 -2
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -1
- 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/edit/state/SaveModal.tsx +1 -1
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +8 -3
- package/templates/src/components/form/DateTimeInput.tsx +10 -3
- package/templates/src/components/form/FileUpload.tsx +11 -5
- package/templates/src/components/form/NumberInput.tsx +2 -2
- package/templates/src/components/form/advanced/APIConfigSection.tsx +208 -2
- package/templates/src/components/form/brand/SiteConfigSection.tsx +10 -0
- 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 +252 -110
- package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +2 -2
- 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 +88 -56
- package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +14 -4
- package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +14 -5
- package/templates/src/components/storykeep/email-builder/Blocks.tsx +169 -0
- package/templates/src/components/storykeep/email-builder/EmailBuilder.tsx +223 -0
- package/templates/src/components/storykeep/email-builder/PreviewModal.tsx +136 -0
- package/templates/src/components/storykeep/email-builder/PropertyPanel.tsx +154 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard.tsx +104 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Bookings.tsx +419 -0
- package/templates/src/components/storykeep/shopify/ShopifyDashboard_Emails.tsx +105 -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/layouts/Layout.astro +8 -5
- 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 +97 -25
- package/templates/src/types/formTypes.ts +4 -2
- package/templates/src/types/tractstack.ts +59 -2
- 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/brandConfig.ts +2 -0
- package/templates/src/utils/api/brandHelpers.ts +26 -0
- package/templates/src/utils/api/emailHelpers.ts +105 -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 +3 -2
- package/utils/inject-files.ts +116 -5
- package/templates/custom/shopify/CalDotComBooking.tsx +0 -44
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
2
|
import { useStore } from '@nanostores/react';
|
|
3
|
+
import { classNames } from '@/utils/helpers';
|
|
3
4
|
import { epinetCustomFilters } from '@/stores/analytics';
|
|
4
5
|
import { Accordion } from '@ark-ui/react';
|
|
5
6
|
import ChevronLeftIcon from '@heroicons/react/24/outline/ChevronLeftIcon';
|
|
@@ -68,6 +69,7 @@ const EpinetTableView = ({
|
|
|
68
69
|
const [currentDay, setCurrentDay] = useState<string | null>(null);
|
|
69
70
|
const [availableDays, setAvailableDays] = useState<string[]>([]);
|
|
70
71
|
const [currentDayIndex, setCurrentDayIndex] = useState(0);
|
|
72
|
+
const [openAccordionValues, setOpenAccordionValues] = useState<string[]>([]);
|
|
71
73
|
|
|
72
74
|
const getContentInfo = (
|
|
73
75
|
contentId: string
|
|
@@ -215,6 +217,12 @@ const EpinetTableView = ({
|
|
|
215
217
|
setCurrentDay(availableDays[newIndex]);
|
|
216
218
|
};
|
|
217
219
|
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
setOpenAccordionValues((prev) =>
|
|
222
|
+
prev.filter((v) => !v.startsWith('empty-'))
|
|
223
|
+
);
|
|
224
|
+
}, [currentDay]);
|
|
225
|
+
|
|
218
226
|
const getCurrentDayData = (): {
|
|
219
227
|
data: HourData[];
|
|
220
228
|
dailyTotal: number;
|
|
@@ -485,129 +493,162 @@ const EpinetTableView = ({
|
|
|
485
493
|
</div>
|
|
486
494
|
</div>
|
|
487
495
|
|
|
488
|
-
<Accordion.Root
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
496
|
+
<Accordion.Root
|
|
497
|
+
multiple
|
|
498
|
+
className="w-full"
|
|
499
|
+
value={openAccordionValues}
|
|
500
|
+
onValueChange={({ value }) =>
|
|
501
|
+
setOpenAccordionValues(value.filter((v) => !v.startsWith('empty-')))
|
|
502
|
+
}
|
|
503
|
+
>
|
|
504
|
+
{dayData.map((item, index) => {
|
|
505
|
+
const itemValue =
|
|
506
|
+
item.type === 'active' ? item.hourKey : `empty-${index}`;
|
|
507
|
+
const isExpandable = item.type === 'active';
|
|
508
|
+
const isOpen = openAccordionValues.includes(itemValue);
|
|
509
|
+
return (
|
|
510
|
+
<Accordion.Item
|
|
511
|
+
key={item.type === 'active' ? item.hourKey : `empty-${index}`}
|
|
512
|
+
value={itemValue}
|
|
513
|
+
disabled={!isExpandable}
|
|
514
|
+
className="border-b border-gray-100 last:border-b-0"
|
|
515
|
+
>
|
|
516
|
+
<Accordion.ItemTrigger
|
|
517
|
+
className={classNames(
|
|
518
|
+
'flex w-full items-center justify-between p-3 text-left transition-colors duration-200',
|
|
519
|
+
isExpandable
|
|
520
|
+
? 'cursor-pointer hover:bg-gray-100'
|
|
521
|
+
: 'cursor-default hover:bg-transparent'
|
|
522
|
+
)}
|
|
523
|
+
>
|
|
524
|
+
{item.type === 'active' ? (
|
|
525
|
+
<div className="flex flex-grow items-center justify-between space-x-3">
|
|
526
|
+
<div className="flex flex-grow items-center space-x-3">
|
|
527
|
+
<span className="text-sm font-bold text-gray-700">
|
|
528
|
+
{item.humanReadableTime}
|
|
529
|
+
</span>
|
|
530
|
+
<span className="text-xs text-gray-600">
|
|
531
|
+
{item.hourlyTotal} event
|
|
532
|
+
{item.hourlyTotal !== 1 ? 's' : ''} /{' '}
|
|
533
|
+
{item.hourlyVisitors} visitor
|
|
534
|
+
{item.hourlyVisitors !== 1 ? 's' : ''}
|
|
535
|
+
</span>
|
|
536
|
+
<div className="relative h-2 w-full max-w-48 rounded bg-gray-200">
|
|
537
|
+
<div
|
|
538
|
+
className="absolute left-0 top-0 h-2 rounded bg-cyan-600"
|
|
539
|
+
style={{
|
|
540
|
+
width: `${Math.max(item.relativeToMax * 100, 5)}%`,
|
|
541
|
+
}}
|
|
542
|
+
title={`${item.hourlyTotal} events (${(
|
|
543
|
+
item.relativeToMax * 100
|
|
544
|
+
).toFixed(1)}% of busiest hour)`}
|
|
545
|
+
/>
|
|
546
|
+
</div>
|
|
518
547
|
</div>
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
onClick={(e) => {
|
|
523
|
-
e.stopPropagation();
|
|
524
|
-
focusOnThisHour(item.hourKey);
|
|
525
|
-
}}
|
|
526
|
-
className="flex cursor-pointer items-center rounded-md bg-orange-100 px-2 py-1 text-xs font-bold text-orange-800 transition-colors duration-200 hover:bg-orange-200"
|
|
527
|
-
role="button"
|
|
528
|
-
tabIndex={0}
|
|
529
|
-
onKeyDown={(e) => {
|
|
530
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
531
|
-
e.preventDefault();
|
|
548
|
+
<div className="flex items-center space-x-2">
|
|
549
|
+
<div
|
|
550
|
+
onClick={(e) => {
|
|
532
551
|
e.stopPropagation();
|
|
533
552
|
focusOnThisHour(item.hourKey);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
553
|
+
}}
|
|
554
|
+
className="flex cursor-pointer items-center rounded-md bg-orange-100 px-2 py-1 text-xs font-bold text-orange-800 transition-colors duration-200 hover:bg-orange-200"
|
|
555
|
+
role="button"
|
|
556
|
+
tabIndex={0}
|
|
557
|
+
onKeyDown={(e) => {
|
|
558
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
559
|
+
e.preventDefault();
|
|
560
|
+
e.stopPropagation();
|
|
561
|
+
focusOnThisHour(item.hourKey);
|
|
562
|
+
}
|
|
563
|
+
}}
|
|
564
|
+
>
|
|
565
|
+
<MagnifyingGlassIcon className="mr-1 h-3 w-3" />
|
|
566
|
+
Journeys this Hour
|
|
567
|
+
</div>
|
|
568
|
+
<div className="flex items-center rounded-md bg-orange-100 px-2 py-1 text-xs font-bold text-orange-800">
|
|
569
|
+
<Accordion.ItemIndicator>
|
|
570
|
+
<ChevronDownIcon
|
|
571
|
+
className={classNames(
|
|
572
|
+
'h-3 w-3 transition-transform duration-200',
|
|
573
|
+
isOpen && 'rotate-180'
|
|
574
|
+
)}
|
|
575
|
+
/>
|
|
576
|
+
</Accordion.ItemIndicator>
|
|
577
|
+
<span
|
|
578
|
+
className={classNames(
|
|
579
|
+
'ml-1',
|
|
580
|
+
isOpen ? 'hidden' : 'block'
|
|
581
|
+
)}
|
|
582
|
+
>
|
|
583
|
+
Expand Details
|
|
584
|
+
</span>
|
|
585
|
+
<span
|
|
586
|
+
className={classNames(
|
|
587
|
+
'ml-1',
|
|
588
|
+
isOpen ? 'block' : 'hidden'
|
|
589
|
+
)}
|
|
590
|
+
>
|
|
591
|
+
Hide Details
|
|
592
|
+
</span>
|
|
593
|
+
</div>
|
|
539
594
|
</div>
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
<span className="
|
|
545
|
-
|
|
595
|
+
</div>
|
|
596
|
+
) : (
|
|
597
|
+
<div className="flex flex-grow items-center">
|
|
598
|
+
<div className="flex items-center">
|
|
599
|
+
<span className="text-sm text-gray-700">
|
|
600
|
+
{item.humanReadableDisplay}
|
|
546
601
|
</span>
|
|
547
|
-
<span className="ml-
|
|
548
|
-
|
|
602
|
+
<span className="ml-2 text-xs italic text-gray-500">
|
|
603
|
+
{item.isFuture ? 'The future awaits!' : 'No activity'}
|
|
549
604
|
</span>
|
|
550
605
|
</div>
|
|
551
606
|
</div>
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
)}
|
|
568
|
-
</Accordion.ItemTrigger>
|
|
569
|
-
|
|
570
|
-
<Accordion.ItemContent className="p-4">
|
|
571
|
-
{item.type === 'active' && (
|
|
572
|
-
<div className="space-y-4">
|
|
573
|
-
{item.contentItems.map((content) => (
|
|
574
|
-
<div
|
|
575
|
-
key={`${item.hourKey}-${content.contentId}`}
|
|
576
|
-
className="mb-3"
|
|
577
|
-
>
|
|
578
|
-
<div className="mb-1 flex items-center justify-between text-sm font-bold text-gray-700">
|
|
579
|
-
<div className="flex items-center">
|
|
580
|
-
{getContentIcon(content.contentType)}
|
|
581
|
-
{content.title}
|
|
582
|
-
</div>
|
|
583
|
-
{content.visitorIds.length > 0 && (
|
|
584
|
-
<div
|
|
585
|
-
className="flex items-center text-xs text-gray-600"
|
|
586
|
-
title={content.visitorIds.join(', ')}
|
|
587
|
-
>
|
|
588
|
-
<UserGroupIcon className="mr-1 h-3 w-3" />
|
|
589
|
-
{content.visitorIds.length} unique visitor
|
|
590
|
-
{content.visitorIds.length !== 1 ? 's' : ''}
|
|
591
|
-
</div>
|
|
592
|
-
)}
|
|
593
|
-
</div>
|
|
594
|
-
<div className="flex flex-wrap gap-2">
|
|
595
|
-
{content.events.map((event, eventIdx) => (
|
|
596
|
-
<div
|
|
597
|
-
key={`${item.hourKey}-${content.contentId}-${event.verb}-${eventIdx}`}
|
|
598
|
-
className="rounded-full bg-cyan-100 px-2 py-0.5 text-xs font-bold text-cyan-800"
|
|
599
|
-
>
|
|
600
|
-
{event.verb} [{event.count}]
|
|
607
|
+
)}
|
|
608
|
+
</Accordion.ItemTrigger>
|
|
609
|
+
|
|
610
|
+
{isExpandable && (
|
|
611
|
+
<Accordion.ItemContent className="p-4">
|
|
612
|
+
<div className="space-y-4">
|
|
613
|
+
{item.contentItems.map((content) => (
|
|
614
|
+
<div
|
|
615
|
+
key={`${item.hourKey}-${content.contentId}`}
|
|
616
|
+
className="mb-3"
|
|
617
|
+
>
|
|
618
|
+
<div className="mb-1 flex items-center justify-between text-sm font-bold text-gray-700">
|
|
619
|
+
<div className="flex items-center">
|
|
620
|
+
{getContentIcon(content.contentType)}
|
|
621
|
+
{content.title}
|
|
601
622
|
</div>
|
|
602
|
-
|
|
623
|
+
{content.visitorIds.length > 0 && (
|
|
624
|
+
<div
|
|
625
|
+
className="flex items-center text-xs text-gray-600"
|
|
626
|
+
title={content.visitorIds.join(', ')}
|
|
627
|
+
>
|
|
628
|
+
<UserGroupIcon className="mr-1 h-3 w-3" />
|
|
629
|
+
{content.visitorIds.length} unique visitor
|
|
630
|
+
{content.visitorIds.length !== 1 ? 's' : ''}
|
|
631
|
+
</div>
|
|
632
|
+
)}
|
|
633
|
+
</div>
|
|
634
|
+
<div className="flex flex-wrap gap-2">
|
|
635
|
+
{content.events.map((event, eventIdx) => (
|
|
636
|
+
<div
|
|
637
|
+
key={`${item.hourKey}-${content.contentId}-${event.verb}-${eventIdx}`}
|
|
638
|
+
className="rounded-full bg-cyan-100 px-2 py-0.5 text-xs font-bold text-cyan-800"
|
|
639
|
+
>
|
|
640
|
+
{event.verb} [{event.count}]
|
|
641
|
+
</div>
|
|
642
|
+
))}
|
|
643
|
+
</div>
|
|
603
644
|
</div>
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
</
|
|
645
|
+
))}
|
|
646
|
+
</div>
|
|
647
|
+
</Accordion.ItemContent>
|
|
607
648
|
)}
|
|
608
|
-
</Accordion.
|
|
609
|
-
|
|
610
|
-
)
|
|
649
|
+
</Accordion.Item>
|
|
650
|
+
);
|
|
651
|
+
})}
|
|
611
652
|
|
|
612
653
|
{dayData.length === 0 && (
|
|
613
654
|
<div className="py-6 text-center text-sm text-gray-500">
|
|
@@ -269,7 +269,10 @@ const EpinetWrapper = ({
|
|
|
269
269
|
return (
|
|
270
270
|
<div className="flex h-96 w-full items-center justify-center rounded bg-gray-100">
|
|
271
271
|
<div className="text-center">
|
|
272
|
-
<div
|
|
272
|
+
<div
|
|
273
|
+
className="inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-cyan-600 border-r-transparent motion-reduce:animate-none"
|
|
274
|
+
style={{ verticalAlign: '-0.125em' }}
|
|
275
|
+
></div>
|
|
273
276
|
<p className="mt-4 text-sm text-gray-600">
|
|
274
277
|
Discovering analytics configuration...
|
|
275
278
|
</p>
|
|
@@ -22,6 +22,13 @@ const comboboxItemStyles = `
|
|
|
22
22
|
.combo-item[data-state="checked"] .check-indicator {
|
|
23
23
|
display: flex;
|
|
24
24
|
}
|
|
25
|
+
.combo-item[data-highlighted] {
|
|
26
|
+
background-color: #0891b2;
|
|
27
|
+
color: #fff;
|
|
28
|
+
}
|
|
29
|
+
.combo-item[data-highlighted] .check-indicator {
|
|
30
|
+
color: #fff;
|
|
31
|
+
}
|
|
25
32
|
`;
|
|
26
33
|
|
|
27
34
|
const FeaturedArticleSetup = ({
|
|
@@ -268,7 +275,7 @@ const FeaturedArticleSetup = ({
|
|
|
268
275
|
<Combobox.Item
|
|
269
276
|
key={item.slug}
|
|
270
277
|
item={item}
|
|
271
|
-
className="combo-item relative cursor-default select-none py-2 pl-10 pr-4 text-gray-900
|
|
278
|
+
className="combo-item relative cursor-default select-none py-2 pl-10 pr-4 text-gray-900"
|
|
272
279
|
>
|
|
273
280
|
<span className="block truncate">{item.title}</span>
|
|
274
281
|
<span className="check-indicator absolute inset-y-0 left-0 flex items-center pl-3 text-cyan-600">
|
|
@@ -7,6 +7,13 @@ import { fullContentMapStore } from '@/stores/storykeep';
|
|
|
7
7
|
import { getCtx } from '@/stores/nodes';
|
|
8
8
|
import type { PaneNode } from '@/types/compositorTypes';
|
|
9
9
|
|
|
10
|
+
const productCardComboItemHighlightStyles = `
|
|
11
|
+
.product-card-combo-item[data-highlighted] {
|
|
12
|
+
background-color: #0891b2;
|
|
13
|
+
color: #fff;
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
|
|
10
17
|
interface ProductCardSetupProps {
|
|
11
18
|
nodeId: string;
|
|
12
19
|
params: Record<string, any> | null;
|
|
@@ -118,6 +125,7 @@ export const ProductCardSetup = (props: ProductCardSetupProps) => {
|
|
|
118
125
|
<Combobox.Label className="text-sm font-bold text-gray-700">
|
|
119
126
|
Find a product
|
|
120
127
|
</Combobox.Label>
|
|
128
|
+
<style>{productCardComboItemHighlightStyles}</style>
|
|
121
129
|
<Combobox.Control>
|
|
122
130
|
<Combobox.Input
|
|
123
131
|
className="w-full rounded-md border-gray-300 px-3 py-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 md:text-sm"
|
|
@@ -131,7 +139,7 @@ export const ProductCardSetup = (props: ProductCardSetupProps) => {
|
|
|
131
139
|
<Combobox.Item
|
|
132
140
|
key={item.value}
|
|
133
141
|
item={item}
|
|
134
|
-
className="relative cursor-pointer select-none px-4 py-2 text-gray-900
|
|
142
|
+
className="product-card-combo-item relative cursor-pointer select-none px-4 py-2 text-gray-900"
|
|
135
143
|
>
|
|
136
144
|
<Combobox.ItemText>{item.label}</Combobox.ItemText>
|
|
137
145
|
</Combobox.Item>
|
|
@@ -35,6 +35,13 @@ const modes = [
|
|
|
35
35
|
},
|
|
36
36
|
];
|
|
37
37
|
|
|
38
|
+
const productGridComboItemHighlightStyles = `
|
|
39
|
+
.product-grid-combo-item[data-highlighted] {
|
|
40
|
+
background-color: #0891b2;
|
|
41
|
+
color: #fff;
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
|
|
38
45
|
export const ProductGridSetup = (props: ProductGridSetupProps) => {
|
|
39
46
|
const { nodeId, params } = props;
|
|
40
47
|
const ctx = getCtx();
|
|
@@ -235,6 +242,7 @@ export const ProductGridSetup = (props: ProductGridSetupProps) => {
|
|
|
235
242
|
<Combobox.Label className="text-sm font-bold text-gray-700">
|
|
236
243
|
Find products to include
|
|
237
244
|
</Combobox.Label>
|
|
245
|
+
<style>{productGridComboItemHighlightStyles}</style>
|
|
238
246
|
<Combobox.Control>
|
|
239
247
|
<Combobox.Input
|
|
240
248
|
className="w-full rounded-md border-gray-300 px-3 py-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 md:text-sm"
|
|
@@ -248,7 +256,7 @@ export const ProductGridSetup = (props: ProductGridSetupProps) => {
|
|
|
248
256
|
<Combobox.Item
|
|
249
257
|
key={item.value}
|
|
250
258
|
item={item}
|
|
251
|
-
className="relative flex cursor-pointer select-none items-center px-4 py-2 text-gray-900
|
|
259
|
+
className="product-grid-combo-item relative flex cursor-pointer select-none items-center px-4 py-2 text-gray-900"
|
|
252
260
|
>
|
|
253
261
|
<Combobox.ItemText>{item.label}</Combobox.ItemText>
|
|
254
262
|
<Combobox.ItemIndicator className="ml-auto">
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { MouseEvent } from 'react';
|
|
1
2
|
import { getCtx } from '@/stores/nodes';
|
|
2
3
|
import { viewportKeyStore } from '@/stores/storykeep';
|
|
3
4
|
import {
|
|
@@ -15,7 +16,7 @@ export const BgPaneWrapper = (props: NodeProps) => {
|
|
|
15
16
|
|
|
16
17
|
const viewport = viewportKeyStore.get().value;
|
|
17
18
|
|
|
18
|
-
const handleClick = (e:
|
|
19
|
+
const handleClick = (e: MouseEvent) => {
|
|
19
20
|
getCtx(props).setClickedNodeId(props.nodeId, true);
|
|
20
21
|
e.stopPropagation();
|
|
21
22
|
};
|
|
@@ -85,7 +85,7 @@ export const GhostInsertBlock = memo((props: GhostInsertBlockProps) => {
|
|
|
85
85
|
setShowInsertOptions(false);
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
const handleClose = (e:
|
|
88
|
+
const handleClose = (e: MouseEvent) => {
|
|
89
89
|
e.stopPropagation();
|
|
90
90
|
getCtx(props).setPanelMode('', '', '');
|
|
91
91
|
setShowInsertOptions(false);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { MouseEvent } from 'react';
|
|
1
2
|
import { useStore } from '@nanostores/react';
|
|
2
3
|
import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
|
|
3
4
|
import { getCtx } from '@/stores/nodes';
|
|
@@ -20,7 +21,7 @@ const AddElementsPanel = ({
|
|
|
20
21
|
ctx.notifyNode('root');
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
const handleMouseDown = (e:
|
|
24
|
+
const handleMouseDown = (e: MouseEvent, mode: ToolAddMode) => {
|
|
24
25
|
e.preventDefault();
|
|
25
26
|
startToolDrag('insert', mode, e.clientX, e.clientY);
|
|
26
27
|
initToolDragListeners();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
1
|
+
import { useState, useEffect, type ChangeEvent } from 'react';
|
|
2
2
|
import ExclamationTriangleIcon from '@heroicons/react/24/outline/ExclamationTriangleIcon';
|
|
3
3
|
import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
|
|
4
4
|
import { getCtx } from '@/stores/nodes';
|
|
@@ -42,7 +42,7 @@ const PaneSlugPanel = ({ nodeId, setMode }: PaneSlugPanelProps) => {
|
|
|
42
42
|
//.replace(/^-+|-+$/g, '');
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
const handleSlugChange = (e:
|
|
45
|
+
const handleSlugChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
46
46
|
const newSlug = validateSlug(e.target.value);
|
|
47
47
|
if (newSlug.length <= 75) {
|
|
48
48
|
// Prevent more than 75 chars
|
|
@@ -103,6 +103,19 @@ const AddPaneCodeHookPanel = ({
|
|
|
103
103
|
parentId: '',
|
|
104
104
|
codeHookTarget: selected,
|
|
105
105
|
isContextPane: isContextPane,
|
|
106
|
+
...(selected === 'shopify-product-grid' ||
|
|
107
|
+
selected === 'shopify-service-list'
|
|
108
|
+
? {
|
|
109
|
+
codeHookPayload: {
|
|
110
|
+
options: JSON.stringify({
|
|
111
|
+
category: 'product|service',
|
|
112
|
+
group: '',
|
|
113
|
+
title: '',
|
|
114
|
+
bgColor: '#f9f9f9',
|
|
115
|
+
}),
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
: {}),
|
|
106
119
|
};
|
|
107
120
|
const targetId =
|
|
108
121
|
isStoryFragment || isContextPane
|
|
@@ -276,9 +276,9 @@ const AddPaneNewPanel = ({
|
|
|
276
276
|
{!hasAssemblyAI && (
|
|
277
277
|
<div className="rounded-lg border-l-4 border-blue-400 bg-blue-50 p-4 shadow-sm">
|
|
278
278
|
<p className="text-sm text-blue-800">
|
|
279
|
-
Tract Stack uses AssemblyAI
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
Tract Stack uses AssemblyAI to generate designs, describe content,
|
|
280
|
+
and streamline the management of your site. We strongly recommend
|
|
281
|
+
enabling these features. See{' '}
|
|
282
282
|
<a
|
|
283
283
|
href="https://freewebpress.org"
|
|
284
284
|
target="_blank"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useState, type ChangeEvent } from 'react';
|
|
2
2
|
|
|
3
3
|
interface AddPaneNewCustomCopyProps {
|
|
4
4
|
value: string;
|
|
@@ -11,7 +11,7 @@ export const AddPaneNewCustomCopy = ({
|
|
|
11
11
|
}: AddPaneNewCustomCopyProps) => {
|
|
12
12
|
const [localValue, setLocalValue] = useState(initialValue);
|
|
13
13
|
|
|
14
|
-
const handleChange = (e:
|
|
14
|
+
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
|
15
15
|
setLocalValue(e.target.value);
|
|
16
16
|
};
|
|
17
17
|
|
|
@@ -11,7 +11,7 @@ import { AiDesignStep, type AiDesignConfig } from './steps/AiDesignStep';
|
|
|
11
11
|
import { AiRefineDesignStep } from './steps/AiRefineDesignStep';
|
|
12
12
|
import prompts from '@/constants/prompts.json';
|
|
13
13
|
import { parseAiPane } from '@/utils/compositor/aiPaneParser';
|
|
14
|
-
import {
|
|
14
|
+
import { callAaiAPI } from '@/utils/compositor/aiGeneration';
|
|
15
15
|
import type { PaneNode, TemplatePane } from '@/types/compositorTypes';
|
|
16
16
|
|
|
17
17
|
interface AiRestylePaneModalProps {
|
|
@@ -87,7 +87,7 @@ export const AiRestylePaneModal = ({
|
|
|
87
87
|
.replace('{{COPY_INPUT}}', 'A generic content section')
|
|
88
88
|
.replace('{{LAYOUT_TYPE}}', 'Text Only');
|
|
89
89
|
|
|
90
|
-
const resultStr = await
|
|
90
|
+
const resultStr = await callAaiAPI({
|
|
91
91
|
prompt: formattedPrompt,
|
|
92
92
|
context: shellPromptDetails.system || '',
|
|
93
93
|
expectJson: true,
|
|
@@ -302,7 +302,7 @@ const ConfigPanePanel = ({
|
|
|
302
302
|
{/* Right Aligned Tools */}
|
|
303
303
|
<div className="ml-auto flex items-center gap-2 px-2">
|
|
304
304
|
{/* Delete & Reorder Tools */}
|
|
305
|
-
{!isTemplate && !isContextPane &&
|
|
305
|
+
{!isTemplate && !isContextPane && (
|
|
306
306
|
<div className="flex items-center gap-1 border-r border-gray-300 pr-2">
|
|
307
307
|
<button
|
|
308
308
|
onClick={handleMoveUp}
|
|
@@ -6,7 +6,7 @@ import ArrowPathRoundedSquareIcon from '@heroicons/react/24/outline/ArrowPathRou
|
|
|
6
6
|
import prompts from '@/constants/prompts.json';
|
|
7
7
|
import { htmlToHtmlAst } from '@/utils/compositor/htmlAst';
|
|
8
8
|
import type { TemplatePane } from '@/types/compositorTypes';
|
|
9
|
-
import {
|
|
9
|
+
import { callAaiAPI } from '@/utils/compositor/aiGeneration';
|
|
10
10
|
import BooleanToggle from '@/components/form/BooleanToggle';
|
|
11
11
|
import { AiDesignStep, type AiDesignConfig } from './AiDesignStep';
|
|
12
12
|
|
|
@@ -86,7 +86,7 @@ export const AiCreativeDesignStep = ({
|
|
|
86
86
|
userPrompt = userPrompt.replace('{{DESIGN_NOTES}}', combinedNotes);
|
|
87
87
|
|
|
88
88
|
// Use shared infrastructure utility
|
|
89
|
-
const rawHtml = await
|
|
89
|
+
const rawHtml = await callAaiAPI({
|
|
90
90
|
prompt: userPrompt,
|
|
91
91
|
context: systemPrompt,
|
|
92
92
|
expectJson: false,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
convertTemplateToAIShell,
|
|
7
7
|
} from '@/utils/compositor/designLibraryHelper';
|
|
8
8
|
import { parseAiPane, parseAiCopyHtml } from '@/utils/compositor/aiPaneParser';
|
|
9
|
-
import {
|
|
9
|
+
import { callAaiAPI } from '@/utils/compositor/aiGeneration';
|
|
10
10
|
import { CopyInputStep, type CopyMode } from './CopyInputStep';
|
|
11
11
|
import type { DesignLibraryEntry } from '@/types/tractstack';
|
|
12
12
|
import type { TemplatePane } from '@/types/compositorTypes';
|
|
@@ -159,7 +159,7 @@ export const AiLibraryCopyStep = ({
|
|
|
159
159
|
.replace('{{LAYOUT_TYPE}}', layoutType)
|
|
160
160
|
.replace('{{COLUMN_EXAMPLE}}', columnPreset.example);
|
|
161
161
|
|
|
162
|
-
const copyResult = await
|
|
162
|
+
const copyResult = await callAaiAPI({
|
|
163
163
|
prompt: formattedCopyPrompt,
|
|
164
164
|
context: copyPromptDetails.system || '',
|
|
165
165
|
expectJson: false,
|
|
@@ -201,7 +201,7 @@ export const AiLibraryCopyStep = ({
|
|
|
201
201
|
.replace('{{LAYOUT_TYPE}}', layoutType)
|
|
202
202
|
.replace('{{SHELL_JSON}}', shellResult);
|
|
203
203
|
|
|
204
|
-
const copyResult = await
|
|
204
|
+
const copyResult = await callAaiAPI({
|
|
205
205
|
prompt: formattedCopyPrompt,
|
|
206
206
|
context: copyPromptDetails.system || '',
|
|
207
207
|
expectJson: false,
|
|
@@ -5,7 +5,7 @@ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
|
|
|
5
5
|
import ArrowPathRoundedSquareIcon from '@heroicons/react/24/outline/ArrowPathRoundedSquareIcon';
|
|
6
6
|
import prompts from '@/constants/prompts.json';
|
|
7
7
|
import { htmlToHtmlAst, cleanHtml } from '@/utils/compositor/htmlAst';
|
|
8
|
-
import {
|
|
8
|
+
import { callAaiAPI } from '@/utils/compositor/aiGeneration';
|
|
9
9
|
import type { TemplatePane } from '@/types/compositorTypes';
|
|
10
10
|
|
|
11
11
|
interface AiRefineDesignStepProps {
|
|
@@ -54,7 +54,7 @@ export const AiRefineDesignStep = ({
|
|
|
54
54
|
userPrompt = userPrompt.replace('{{HTML_INPUT}}', cleanHtml(initialHtml));
|
|
55
55
|
|
|
56
56
|
// 1. Get RAW output from AI
|
|
57
|
-
const resultHtml = await
|
|
57
|
+
const resultHtml = await callAaiAPI({
|
|
58
58
|
prompt: userPrompt,
|
|
59
59
|
context: systemPrompt,
|
|
60
60
|
expectJson: false,
|