astro-tractstack 2.0.0-rc.0
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/LICENSE +110 -0
- package/README.md +56 -0
- package/astro.d.ts +64 -0
- package/bin/create-tractstack.js +483 -0
- package/dist/config.js +80 -0
- package/dist/index.js +2129 -0
- package/package.json +89 -0
- package/templates/artpacks/kCz/captainBreakfast_1080px.webp +0 -0
- package/templates/artpacks/kCz/captainBreakfast_1920px.webp +0 -0
- package/templates/artpacks/kCz/captainBreakfast_600px.webp +0 -0
- package/templates/artpacks/kCz/cleanDrips_1080px.webp +0 -0
- package/templates/artpacks/kCz/cleanDrips_1920px.webp +0 -0
- package/templates/artpacks/kCz/cleanDrips_600px.webp +0 -0
- package/templates/artpacks/kCz/crispwaves_1080px.webp +0 -0
- package/templates/artpacks/kCz/crispwaves_1920px.webp +0 -0
- package/templates/artpacks/kCz/crispwaves_600px.webp +0 -0
- package/templates/artpacks/kCz/dragonSkin_1080px.webp +0 -0
- package/templates/artpacks/kCz/dragonSkin_1920px.webp +0 -0
- package/templates/artpacks/kCz/dragonSkin_600px.webp +0 -0
- package/templates/artpacks/kCz/dragon_1080px.webp +0 -0
- package/templates/artpacks/kCz/dragon_1920px.webp +0 -0
- package/templates/artpacks/kCz/dragon_600px.webp +0 -0
- package/templates/artpacks/kCz/nightcity_1080px.webp +0 -0
- package/templates/artpacks/kCz/nightcity_1920px.webp +0 -0
- package/templates/artpacks/kCz/nightcity_600px.webp +0 -0
- package/templates/artpacks/kCz/pattern1_1080px.webp +0 -0
- package/templates/artpacks/kCz/pattern1_1920px.webp +0 -0
- package/templates/artpacks/kCz/pattern1_600px.webp +0 -0
- package/templates/artpacks/kCz/pattern2_1080px.webp +0 -0
- package/templates/artpacks/kCz/pattern2_1920px.webp +0 -0
- package/templates/artpacks/kCz/pattern2_600px.webp +0 -0
- package/templates/artpacks/kCz/skindrips_1080px.webp +0 -0
- package/templates/artpacks/kCz/skindrips_1920px.webp +0 -0
- package/templates/artpacks/kCz/skindrips_600px.webp +0 -0
- package/templates/artpacks/kCz/slimetime_1080px.webp +0 -0
- package/templates/artpacks/kCz/slimetime_1920px.webp +0 -0
- package/templates/artpacks/kCz/slimetime_600px.webp +0 -0
- package/templates/artpacks/kCz/snake_1080px.webp +0 -0
- package/templates/artpacks/kCz/snake_1920px.webp +0 -0
- package/templates/artpacks/kCz/snake_600px.webp +0 -0
- package/templates/artpacks/kCz/toxicshock_1080px.webp +0 -0
- package/templates/artpacks/kCz/toxicshock_1920px.webp +0 -0
- package/templates/artpacks/kCz/toxicshock_600px.webp +0 -0
- package/templates/artpacks/kCz/tractstack_1080px.webp +0 -0
- package/templates/artpacks/kCz/tractstack_1920px.webp +0 -0
- package/templates/artpacks/kCz/tractstack_600px.webp +0 -0
- package/templates/artpacks/kCz/tripdrips_1080px.webp +0 -0
- package/templates/artpacks/kCz/tripdrips_1920px.webp +0 -0
- package/templates/artpacks/kCz/tripdrips_600px.webp +0 -0
- package/templates/artpacks/kCz/wavedrips_1080px.webp +0 -0
- package/templates/artpacks/kCz/wavedrips_1920px.webp +0 -0
- package/templates/artpacks/kCz/wavedrips_600px.webp +0 -0
- package/templates/artpacks/t8k/beach_1080px.webp +0 -0
- package/templates/artpacks/t8k/beach_1920px.webp +0 -0
- package/templates/artpacks/t8k/beach_600px.webp +0 -0
- package/templates/artpacks/t8k/blast_1080px.webp +0 -0
- package/templates/artpacks/t8k/blast_1920px.webp +0 -0
- package/templates/artpacks/t8k/blast_600px.webp +0 -0
- package/templates/artpacks/t8k/bokeh_1080px.webp +0 -0
- package/templates/artpacks/t8k/bokeh_1920px.webp +0 -0
- package/templates/artpacks/t8k/bokeh_600px.webp +0 -0
- package/templates/artpacks/t8k/cartoon_1080px.webp +0 -0
- package/templates/artpacks/t8k/cartoon_1920px.webp +0 -0
- package/templates/artpacks/t8k/cartoon_600px.webp +0 -0
- package/templates/artpacks/t8k/darkeggshell_1080px.webp +0 -0
- package/templates/artpacks/t8k/darkeggshell_1920px.webp +0 -0
- package/templates/artpacks/t8k/darkeggshell_600px.webp +0 -0
- package/templates/artpacks/t8k/explosion_1080px.webp +0 -0
- package/templates/artpacks/t8k/explosion_1920px.webp +0 -0
- package/templates/artpacks/t8k/explosion_600px.webp +0 -0
- package/templates/artpacks/t8k/floral_1080px.webp +0 -0
- package/templates/artpacks/t8k/floral_1920px.webp +0 -0
- package/templates/artpacks/t8k/floral_600px.webp +0 -0
- package/templates/artpacks/t8k/flower_1080px.webp +0 -0
- package/templates/artpacks/t8k/flower_1920px.webp +0 -0
- package/templates/artpacks/t8k/flower_600px.webp +0 -0
- package/templates/artpacks/t8k/foliage_1080px.webp +0 -0
- package/templates/artpacks/t8k/foliage_1920px.webp +0 -0
- package/templates/artpacks/t8k/foliage_600px.webp +0 -0
- package/templates/artpacks/t8k/mist_1080px.webp +0 -0
- package/templates/artpacks/t8k/mist_1920px.webp +0 -0
- package/templates/artpacks/t8k/mist_600px.webp +0 -0
- package/templates/artpacks/t8k/portal_1080px.webp +0 -0
- package/templates/artpacks/t8k/portal_1920px.webp +0 -0
- package/templates/artpacks/t8k/portal_600px.webp +0 -0
- package/templates/artpacks/t8k/storytime_1080px.webp +0 -0
- package/templates/artpacks/t8k/storytime_1920px.webp +0 -0
- package/templates/artpacks/t8k/storytime_600px.webp +0 -0
- package/templates/artpacks/t8k/tacky_1080px.webp +0 -0
- package/templates/artpacks/t8k/tacky_1920px.webp +0 -0
- package/templates/artpacks/t8k/tacky_600px.webp +0 -0
- package/templates/artpacks/t8k/wallpaper_1080px.webp +0 -0
- package/templates/artpacks/t8k/wallpaper_1920px.webp +0 -0
- package/templates/artpacks/t8k/wallpaper_600px.webp +0 -0
- package/templates/brand/favicon.ico +0 -0
- package/templates/brand/logo.svg +19 -0
- package/templates/brand/static.jpg +0 -0
- package/templates/brand/wordmark.svg +4 -0
- package/templates/css/custom.css +51 -0
- package/templates/css/frontend.css +3519 -0
- package/templates/css/storykeep.css +92872 -0
- package/templates/custom/minimal/CodeHook.astro +53 -0
- package/templates/custom/minimal/CustomRoutes.astro +46 -0
- package/templates/custom/with-examples/CodeHook.astro +49 -0
- package/templates/custom/with-examples/CustomHero.astro +13 -0
- package/templates/custom/with-examples/CustomRoutes.astro +39 -0
- package/templates/custom/with-examples/pages/Collections.astro +110 -0
- package/templates/env.example +8 -0
- package/templates/fonts/Inter-Black.woff2 +0 -0
- package/templates/fonts/Inter-Bold.woff2 +0 -0
- package/templates/fonts/Inter-Regular.woff2 +0 -0
- package/templates/icons/h2.svg +1 -0
- package/templates/icons/h3.svg +1 -0
- package/templates/icons/h4.svg +1 -0
- package/templates/icons/h5.svg +1 -0
- package/templates/icons/image.svg +7 -0
- package/templates/icons/text.svg +6 -0
- package/templates/socials/codepen.svg +1 -0
- package/templates/socials/discord.svg +1 -0
- package/templates/socials/facebook.svg +1 -0
- package/templates/socials/github.svg +1 -0
- package/templates/socials/instagram.svg +1 -0
- package/templates/socials/linkedin.svg +1 -0
- package/templates/socials/mail.svg +1 -0
- package/templates/socials/rumble.svg +1 -0
- package/templates/socials/tiktok.svg +1 -0
- package/templates/socials/twitch.svg +1 -0
- package/templates/socials/twitter.svg +1 -0
- package/templates/socials/x.svg +1 -0
- package/templates/socials/youtube.svg +1 -0
- package/templates/src/client/analytics-events.ts +213 -0
- package/templates/src/client/belief-events.ts +205 -0
- package/templates/src/client/sse.ts +667 -0
- package/templates/src/components/Footer.astro +246 -0
- package/templates/src/components/Fragment.astro +70 -0
- package/templates/src/components/Header.astro +458 -0
- package/templates/src/components/Menu.tsx +196 -0
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +692 -0
- package/templates/src/components/codehooks/BunnyVideoWrapper.astro +78 -0
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +1020 -0
- package/templates/src/components/codehooks/EpinetTableView.tsx +594 -0
- package/templates/src/components/codehooks/EpinetWrapper.tsx +424 -0
- package/templates/src/components/codehooks/FeaturedContent.astro +273 -0
- package/templates/src/components/codehooks/FeaturedContentSetup.tsx +738 -0
- package/templates/src/components/codehooks/ListContent.astro +460 -0
- package/templates/src/components/codehooks/ListContentSetup.tsx +649 -0
- package/templates/src/components/codehooks/SankeyDiagram.tsx +359 -0
- package/templates/src/components/compositor/Compositor.tsx +144 -0
- package/templates/src/components/compositor/Node.tsx +415 -0
- package/templates/src/components/compositor/NodeWithGuid.tsx +25 -0
- package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +87 -0
- package/templates/src/components/compositor/elements/Belief.tsx +148 -0
- package/templates/src/components/compositor/elements/BgImage.tsx +118 -0
- package/templates/src/components/compositor/elements/BgVisualBreak.tsx +102 -0
- package/templates/src/components/compositor/elements/BunnyVideo.tsx +63 -0
- package/templates/src/components/compositor/elements/IdentifyAs.tsx +66 -0
- package/templates/src/components/compositor/elements/PlayButton.tsx +19 -0
- package/templates/src/components/compositor/elements/SignUp.tsx +179 -0
- package/templates/src/components/compositor/elements/Svg.tsx +33 -0
- package/templates/src/components/compositor/elements/ToggleBelief.tsx +36 -0
- package/templates/src/components/compositor/elements/YouTubeWrapper.tsx +33 -0
- package/templates/src/components/compositor/nodes/BgPaneWrapper.tsx +35 -0
- package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +189 -0
- package/templates/src/components/compositor/nodes/Markdown.tsx +179 -0
- package/templates/src/components/compositor/nodes/Pane.tsx +277 -0
- package/templates/src/components/compositor/nodes/Pane_eraser.tsx +69 -0
- package/templates/src/components/compositor/nodes/Pane_layout.tsx +77 -0
- package/templates/src/components/compositor/nodes/RenderChildren.tsx +19 -0
- package/templates/src/components/compositor/nodes/StoryFragment.tsx +35 -0
- package/templates/src/components/compositor/nodes/TagElement.tsx +14 -0
- package/templates/src/components/compositor/nodes/Widget.tsx +115 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeA.tsx +4 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeA_eraser.tsx +26 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeAnchorComponent.tsx +248 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +684 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_eraser.tsx +62 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_insert.tsx +120 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_settings.tsx +62 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeButton.tsx +5 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeButton_eraser.tsx +26 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeImg.tsx +28 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeText.tsx +18 -0
- package/templates/src/components/compositor/nodes/tagElements/TabIndicator.tsx +51 -0
- package/templates/src/components/compositor/preview/FeaturedContentPreview.tsx +128 -0
- package/templates/src/components/compositor/preview/ListContentPreview.tsx +213 -0
- package/templates/src/components/compositor/preview/OgImagePreview.tsx +223 -0
- package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +199 -0
- package/templates/src/components/compositor/preview/PanesPreviewGenerator.tsx +123 -0
- package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +154 -0
- package/templates/src/components/edit/Header.tsx +181 -0
- package/templates/src/components/edit/PanelSwitch.tsx +446 -0
- package/templates/src/components/edit/SettingsPanel.tsx +70 -0
- package/templates/src/components/edit/ToolBar.tsx +101 -0
- package/templates/src/components/edit/ToolMode.tsx +121 -0
- package/templates/src/components/edit/context/ContextPaneConfig.tsx +91 -0
- package/templates/src/components/edit/context/ContextPaneConfig_slug.tsx +174 -0
- package/templates/src/components/edit/context/ContextPaneConfig_title.tsx +186 -0
- package/templates/src/components/edit/pane/AddPanePanel.tsx +136 -0
- package/templates/src/components/edit/pane/AddPanePanel_break.tsx +470 -0
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +264 -0
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +623 -0
- package/templates/src/components/edit/pane/AddPanePanel_newAICopy.tsx +107 -0
- package/templates/src/components/edit/pane/AddPanePanel_newAICopy_modal.tsx +217 -0
- package/templates/src/components/edit/pane/AddPanePanel_newCopyMode.tsx +109 -0
- package/templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx +39 -0
- package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +445 -0
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +245 -0
- package/templates/src/components/edit/pane/PageGen.tsx +485 -0
- package/templates/src/components/edit/pane/PageGenSelector.tsx +238 -0
- package/templates/src/components/edit/pane/PageGenSpecial.tsx +362 -0
- package/templates/src/components/edit/pane/PageGen_preview.tsx +495 -0
- package/templates/src/components/edit/pane/PanePanel_impression.tsx +258 -0
- package/templates/src/components/edit/pane/PanePanel_path.tsx +268 -0
- package/templates/src/components/edit/pane/PanePanel_slug.tsx +219 -0
- package/templates/src/components/edit/pane/PanePanel_title.tsx +142 -0
- package/templates/src/components/edit/panels/StyleBreakPanel.tsx +182 -0
- package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +439 -0
- package/templates/src/components/edit/panels/StyleElementPanel.tsx +177 -0
- package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +349 -0
- package/templates/src/components/edit/panels/StyleElementPanel_remove.tsx +159 -0
- package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +320 -0
- package/templates/src/components/edit/panels/StyleImagePanel.tsx +460 -0
- package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +296 -0
- package/templates/src/components/edit/panels/StyleImagePanel_remove.tsx +153 -0
- package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +312 -0
- package/templates/src/components/edit/panels/StyleLiElementPanel.tsx +273 -0
- package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +301 -0
- package/templates/src/components/edit/panels/StyleLiElementPanel_remove.tsx +132 -0
- package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +313 -0
- package/templates/src/components/edit/panels/StyleLinkPanel.tsx +346 -0
- package/templates/src/components/edit/panels/StyleLinkPanel_add.tsx +265 -0
- package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +240 -0
- package/templates/src/components/edit/panels/StyleLinkPanel_remove.tsx +94 -0
- package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +110 -0
- package/templates/src/components/edit/panels/StyleParentPanel.tsx +263 -0
- package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +275 -0
- package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +112 -0
- package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +87 -0
- package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +141 -0
- package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +428 -0
- package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +292 -0
- package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +190 -0
- package/templates/src/components/edit/panels/StyleWidgetPanel_remove.tsx +152 -0
- package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +308 -0
- package/templates/src/components/edit/state/SaveModal.tsx +811 -0
- package/templates/src/components/edit/state/StylesMemory.tsx +310 -0
- package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +289 -0
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +320 -0
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +888 -0
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +269 -0
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_title.tsx +190 -0
- package/templates/src/components/edit/widgets/BeliefWidget.tsx +183 -0
- package/templates/src/components/edit/widgets/BunnyWidget.tsx +134 -0
- package/templates/src/components/edit/widgets/IdentifyAsWidget.tsx +193 -0
- package/templates/src/components/edit/widgets/SignupWidget.tsx +177 -0
- package/templates/src/components/edit/widgets/ToggleWidget.tsx +152 -0
- package/templates/src/components/edit/widgets/YouTubeWidget.tsx +65 -0
- package/templates/src/components/fields/ActionBuilderTimeSelector.tsx +353 -0
- package/templates/src/components/fields/ArtpackImage.tsx +480 -0
- package/templates/src/components/fields/BackgroundImage.tsx +530 -0
- package/templates/src/components/fields/BackgroundImageWrapper.tsx +192 -0
- package/templates/src/components/fields/BooleanParam.tsx +67 -0
- package/templates/src/components/fields/BunnyMomentSelector.tsx +56 -0
- package/templates/src/components/fields/ColorPickerCombo.tsx +284 -0
- package/templates/src/components/fields/ImageUpload.tsx +405 -0
- package/templates/src/components/fields/MultiParam.tsx +75 -0
- package/templates/src/components/fields/PaneBreakCollectionSelector.tsx +97 -0
- package/templates/src/components/fields/PaneBreakShapeSelector.tsx +134 -0
- package/templates/src/components/fields/SelectedTailwindClass.tsx +44 -0
- package/templates/src/components/fields/SingleParam.tsx +73 -0
- package/templates/src/components/fields/ViewportComboBox.tsx +252 -0
- package/templates/src/components/form/ActionBuilderField.tsx +282 -0
- package/templates/src/components/form/ActionBuilderSlugSelector.tsx +182 -0
- package/templates/src/components/form/BooleanToggle.tsx +94 -0
- package/templates/src/components/form/ColorPicker.tsx +153 -0
- package/templates/src/components/form/DateTimeInput.tsx +638 -0
- package/templates/src/components/form/EnumSelect.tsx +88 -0
- package/templates/src/components/form/FileUpload.tsx +465 -0
- package/templates/src/components/form/MagicPathBuilder.tsx +546 -0
- package/templates/src/components/form/NumberInput.tsx +101 -0
- package/templates/src/components/form/ParagraphArrayInput.tsx +207 -0
- package/templates/src/components/form/StringArrayInput.tsx +163 -0
- package/templates/src/components/form/StringInput.tsx +88 -0
- package/templates/src/components/form/UnsavedChangesBar.tsx +295 -0
- package/templates/src/components/form/advanced/APIConfigSection.tsx +69 -0
- package/templates/src/components/form/advanced/AuthConfigSection.tsx +97 -0
- package/templates/src/components/form/brand/BrandAssetsSection.tsx +93 -0
- package/templates/src/components/form/brand/BrandColorsSection.tsx +201 -0
- package/templates/src/components/form/brand/SEOSection.tsx +101 -0
- package/templates/src/components/form/brand/SiteConfigSection.tsx +61 -0
- package/templates/src/components/form/brand/SocialLinksSection.tsx +393 -0
- package/templates/src/components/profile/ProfileConsent.tsx +65 -0
- package/templates/src/components/profile/ProfileCreate.tsx +462 -0
- package/templates/src/components/profile/ProfileEdit.tsx +409 -0
- package/templates/src/components/profile/ProfileSwitch.tsx +255 -0
- package/templates/src/components/profile/ProfileUnlock.tsx +221 -0
- package/templates/src/components/storykeep/Dashboard.tsx +160 -0
- package/templates/src/components/storykeep/Dashboard_Activity.tsx +56 -0
- package/templates/src/components/storykeep/Dashboard_Advanced.tsx +165 -0
- package/templates/src/components/storykeep/Dashboard_Analytics.tsx +451 -0
- package/templates/src/components/storykeep/Dashboard_Branding.tsx +95 -0
- package/templates/src/components/storykeep/Dashboard_Content.tsx +191 -0
- package/templates/src/components/storykeep/controls/UsageCell.tsx +71 -0
- package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +378 -0
- package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +329 -0
- package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +385 -0
- package/templates/src/components/storykeep/controls/content/ContentSummary.tsx +149 -0
- package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +397 -0
- package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +260 -0
- package/templates/src/components/storykeep/controls/content/ManageContent.tsx +439 -0
- package/templates/src/components/storykeep/controls/content/MenuForm.tsx +239 -0
- package/templates/src/components/storykeep/controls/content/MenuTable.tsx +332 -0
- package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +724 -0
- package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +355 -0
- package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +222 -0
- package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +482 -0
- package/templates/src/components/storykeep/state/BrandingWrapper.tsx +42 -0
- package/templates/src/components/storykeep/state/FetchAnalytics.tsx +350 -0
- package/templates/src/components/storykeep/widgets/ResponsiveLine.tsx +319 -0
- package/templates/src/components/storykeep/widgets/Wizard.tsx +278 -0
- package/templates/src/components/tenant/RegistrationForm.tsx +447 -0
- package/templates/src/components/widgets/BunnyVideoHero.astro +775 -0
- package/templates/src/components/widgets/Impression.tsx +102 -0
- package/templates/src/components/widgets/ImpressionWrapper.tsx +214 -0
- package/templates/src/constants/beliefs.ts +61 -0
- package/templates/src/constants/brandThemes.ts +133 -0
- package/templates/src/constants/prompts.json +55 -0
- package/templates/src/constants/shapes.ts +556 -0
- package/templates/src/constants/stopWords.ts +116 -0
- package/templates/src/constants/tailwindColors.json +344 -0
- package/templates/src/constants.ts +274 -0
- package/templates/src/hooks/useFormState.ts +203 -0
- package/templates/src/layouts/Layout.astro +290 -0
- package/templates/src/lib/session.ts +126 -0
- package/templates/src/lib/storyData.ts +56 -0
- package/templates/src/middleware.ts +52 -0
- package/templates/src/pages/404.astro +54 -0
- package/templates/src/pages/[...slug]/edit.astro +216 -0
- package/templates/src/pages/[...slug].astro +148 -0
- package/templates/src/pages/api/auth/decode.ts +101 -0
- package/templates/src/pages/api/auth/login.ts +122 -0
- package/templates/src/pages/api/auth/logout.ts +37 -0
- package/templates/src/pages/api/auth/profile.ts +76 -0
- package/templates/src/pages/api/orphan-analysis.ts +106 -0
- package/templates/src/pages/api/tailwind.ts +116 -0
- package/templates/src/pages/collections/[param1].astro +65 -0
- package/templates/src/pages/context/[...contextSlug]/edit.astro +207 -0
- package/templates/src/pages/context/[...contextSlug].astro +161 -0
- package/templates/src/pages/llms.txt.ts +122 -0
- package/templates/src/pages/maint.astro +183 -0
- package/templates/src/pages/media/[...slug].astro +67 -0
- package/templates/src/pages/robots.txt.ts +36 -0
- package/templates/src/pages/sandbox/activate.astro +258 -0
- package/templates/src/pages/sandbox/register.astro +44 -0
- package/templates/src/pages/sandbox/success.astro +179 -0
- package/templates/src/pages/sitemap.xml.ts +119 -0
- package/templates/src/pages/storykeep/advanced.astro +69 -0
- package/templates/src/pages/storykeep/branding.astro +57 -0
- package/templates/src/pages/storykeep/content.astro +71 -0
- package/templates/src/pages/storykeep/init.astro +36 -0
- package/templates/src/pages/storykeep/login.astro +266 -0
- package/templates/src/pages/storykeep/logout.astro +84 -0
- package/templates/src/pages/storykeep/profile.astro +98 -0
- package/templates/src/pages/storykeep.astro +81 -0
- package/templates/src/stores/analytics.ts +171 -0
- package/templates/src/stores/backend.ts +16 -0
- package/templates/src/stores/navigation.ts +149 -0
- package/templates/src/stores/nodes.ts +2390 -0
- package/templates/src/stores/nodesHistory.ts +85 -0
- package/templates/src/stores/notificationSystem.ts +41 -0
- package/templates/src/stores/orphanAnalysis.ts +409 -0
- package/templates/src/stores/storykeep.ts +247 -0
- package/templates/src/types/astro.ts +86 -0
- package/templates/src/types/compositorTypes.ts +456 -0
- package/templates/src/types/formTypes.ts +281 -0
- package/templates/src/types/multiTenant.ts +77 -0
- package/templates/src/types/nodeProps.ts +66 -0
- package/templates/src/types/tractstack.ts +445 -0
- package/templates/src/utils/aai/getTitleSlug.ts +72 -0
- package/templates/src/utils/actions/actionButton.ts +101 -0
- package/templates/src/utils/actions/lispLexer.ts +57 -0
- package/templates/src/utils/actions/preParse_Action.ts +85 -0
- package/templates/src/utils/actions/preParse_Bunny.ts +50 -0
- package/templates/src/utils/actions/preParse_Clicked.ts +87 -0
- package/templates/src/utils/actions/preParse_Impression.ts +71 -0
- package/templates/src/utils/api/advancedConfig.ts +66 -0
- package/templates/src/utils/api/advancedHelpers.ts +134 -0
- package/templates/src/utils/api/beliefConfig.ts +87 -0
- package/templates/src/utils/api/beliefHelpers.ts +196 -0
- package/templates/src/utils/api/brandConfig.ts +126 -0
- package/templates/src/utils/api/brandHelpers.ts +155 -0
- package/templates/src/utils/api/fileHelpers.ts +306 -0
- package/templates/src/utils/api/menuConfig.ts +57 -0
- package/templates/src/utils/api/menuHelpers.ts +156 -0
- package/templates/src/utils/api/resourceConfig.ts +158 -0
- package/templates/src/utils/api/resourceHelpers.ts +72 -0
- package/templates/src/utils/api/tenantConfig.ts +97 -0
- package/templates/src/utils/api/tenantHelpers.ts +172 -0
- package/templates/src/utils/api.ts +183 -0
- package/templates/src/utils/auth.ts +150 -0
- package/templates/src/utils/backend.ts +243 -0
- package/templates/src/utils/compositor/TemplateMarkdowns.ts +118 -0
- package/templates/src/utils/compositor/TemplateNodes.ts +138 -0
- package/templates/src/utils/compositor/TemplatePanes.ts +100 -0
- package/templates/src/utils/compositor/allowInsert.ts +100 -0
- package/templates/src/utils/compositor/domHelpers.ts +37 -0
- package/templates/src/utils/compositor/handleClickEvent.ts +131 -0
- package/templates/src/utils/compositor/nodesHelper.ts +491 -0
- package/templates/src/utils/compositor/nodesMarkdownGenerator.ts +292 -0
- package/templates/src/utils/compositor/processMarkdown.ts +431 -0
- package/templates/src/utils/compositor/reduceNodesClassNames.ts +192 -0
- package/templates/src/utils/compositor/tailwindClasses.ts +1795 -0
- package/templates/src/utils/compositor/tailwindColors.ts +227 -0
- package/templates/src/utils/compositor/templateMarkdownStyles.ts +1265 -0
- package/templates/src/utils/compositor/typeGuards.ts +193 -0
- package/templates/src/utils/etl/extractor.ts +119 -0
- package/templates/src/utils/etl/index.ts +88 -0
- package/templates/src/utils/etl/loader.ts +36 -0
- package/templates/src/utils/etl/transformer.ts +286 -0
- package/templates/src/utils/helpers.ts +435 -0
- package/templates/src/utils/layout.ts +209 -0
- package/templates/src/utils/profileStorage.ts +306 -0
- package/templates/src/utils/useInterval.ts +27 -0
- package/templates/tailwind.config.cjs +169 -0
- package/utils/create-resolver.ts +10 -0
- package/utils/inject-files.ts +2140 -0
- package/utils/validate-config.ts +43 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { TractStackAPI } from '../api';
|
|
2
|
+
import { convertToBackendFormat, convertToLocalState } from './beliefHelpers';
|
|
3
|
+
import type { BeliefNode, BeliefNodeState } from '@/types/tractstack';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Save an existing belief
|
|
7
|
+
*/
|
|
8
|
+
export async function saveBelief(
|
|
9
|
+
tenantId: string,
|
|
10
|
+
beliefState: BeliefNodeState
|
|
11
|
+
): Promise<BeliefNode> {
|
|
12
|
+
const api = new TractStackAPI(tenantId);
|
|
13
|
+
const beliefData = convertToBackendFormat(beliefState);
|
|
14
|
+
|
|
15
|
+
const response = await api.put(
|
|
16
|
+
`/api/v1/nodes/beliefs/${beliefState.id}`,
|
|
17
|
+
beliefData
|
|
18
|
+
);
|
|
19
|
+
return response.data as BeliefNode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a new belief
|
|
24
|
+
*/
|
|
25
|
+
export async function createBelief(
|
|
26
|
+
tenantId: string,
|
|
27
|
+
beliefState: BeliefNodeState
|
|
28
|
+
): Promise<BeliefNode> {
|
|
29
|
+
const api = new TractStackAPI(tenantId);
|
|
30
|
+
const beliefData = convertToBackendFormat(beliefState);
|
|
31
|
+
|
|
32
|
+
const response = await api.post('/api/v1/nodes/beliefs/create', beliefData);
|
|
33
|
+
return response.data as BeliefNode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Delete a belief
|
|
38
|
+
*/
|
|
39
|
+
export async function deleteBelief(
|
|
40
|
+
tenantId: string,
|
|
41
|
+
beliefId: string
|
|
42
|
+
): Promise<void> {
|
|
43
|
+
const api = new TractStackAPI(tenantId);
|
|
44
|
+
await api.request(`/api/v1/nodes/beliefs/${beliefId}`, { method: 'DELETE' });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get a belief by ID
|
|
49
|
+
*/
|
|
50
|
+
export async function getBeliefById(
|
|
51
|
+
tenantId: string,
|
|
52
|
+
beliefId: string
|
|
53
|
+
): Promise<BeliefNode> {
|
|
54
|
+
const api = new TractStackAPI(tenantId);
|
|
55
|
+
const response = await api.get(`/api/v1/nodes/beliefs/${beliefId}`);
|
|
56
|
+
return response.data as BeliefNode;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Main save workflow with state update
|
|
61
|
+
* Following the exact pattern from menuConfig.ts
|
|
62
|
+
*/
|
|
63
|
+
export async function saveBeliefWithStateUpdate(
|
|
64
|
+
tenantId: string,
|
|
65
|
+
currentState: BeliefNodeState
|
|
66
|
+
): Promise<BeliefNodeState> {
|
|
67
|
+
try {
|
|
68
|
+
let savedBelief: BeliefNode;
|
|
69
|
+
|
|
70
|
+
// Determine if this is a create or update operation
|
|
71
|
+
const isCreate = !currentState.id || currentState.id === '';
|
|
72
|
+
|
|
73
|
+
if (isCreate) {
|
|
74
|
+
// Generate temporary ID for create (backend will assign real ID)
|
|
75
|
+
const tempState = { ...currentState, id: crypto.randomUUID() };
|
|
76
|
+
savedBelief = await createBelief(tenantId, tempState);
|
|
77
|
+
} else {
|
|
78
|
+
savedBelief = await saveBelief(tenantId, currentState);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Convert the saved belief back to state format
|
|
82
|
+
return convertToLocalState(savedBelief);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error('Belief save failed:', error);
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BeliefNode,
|
|
3
|
+
BeliefNodeState,
|
|
4
|
+
FieldErrors,
|
|
5
|
+
} from '@/types/tractstack';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Convert backend BeliefNode to frontend BeliefNodeState
|
|
9
|
+
*/
|
|
10
|
+
export function convertToLocalState(beliefNode: BeliefNode): BeliefNodeState {
|
|
11
|
+
return {
|
|
12
|
+
id: beliefNode.id,
|
|
13
|
+
title: beliefNode.title,
|
|
14
|
+
slug: beliefNode.slug,
|
|
15
|
+
scale: beliefNode.scale,
|
|
16
|
+
customValues: beliefNode.customValues || [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Convert frontend BeliefNodeState to backend BeliefNode format
|
|
22
|
+
*/
|
|
23
|
+
export function convertToBackendFormat(state: BeliefNodeState): BeliefNode {
|
|
24
|
+
return {
|
|
25
|
+
id: state.id,
|
|
26
|
+
title: state.title,
|
|
27
|
+
slug: state.slug,
|
|
28
|
+
scale: state.scale,
|
|
29
|
+
customValues:
|
|
30
|
+
state.customValues.length > 0 ? state.customValues : undefined,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate belief node state
|
|
36
|
+
*/
|
|
37
|
+
export function validateBeliefNode(state: BeliefNodeState): FieldErrors {
|
|
38
|
+
const errors: FieldErrors = {};
|
|
39
|
+
|
|
40
|
+
// Validate title
|
|
41
|
+
if (!state.title?.trim()) {
|
|
42
|
+
errors.title = 'Title is required';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Validate slug
|
|
46
|
+
if (!state.slug?.trim()) {
|
|
47
|
+
errors.slug = 'Slug is required';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Validate scale
|
|
51
|
+
if (!state.scale?.trim()) {
|
|
52
|
+
errors.scale = 'Scale is required';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Validate custom values if scale is custom
|
|
56
|
+
if (state.scale === 'custom') {
|
|
57
|
+
if (!state.customValues || state.customValues.length === 0) {
|
|
58
|
+
errors.customValues =
|
|
59
|
+
'At least one custom value is required for custom scale';
|
|
60
|
+
} else {
|
|
61
|
+
state.customValues.forEach((value, index) => {
|
|
62
|
+
if (!value?.trim()) {
|
|
63
|
+
errors[`customValues.${index}`] = 'Custom value cannot be empty';
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return errors;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* State interceptor for form state management
|
|
74
|
+
*/
|
|
75
|
+
export function beliefStateIntercept(
|
|
76
|
+
state: BeliefNodeState,
|
|
77
|
+
field: keyof BeliefNodeState,
|
|
78
|
+
value: any
|
|
79
|
+
): BeliefNodeState {
|
|
80
|
+
const newState = { ...state };
|
|
81
|
+
|
|
82
|
+
switch (field) {
|
|
83
|
+
case 'title':
|
|
84
|
+
newState.title = value || '';
|
|
85
|
+
break;
|
|
86
|
+
case 'slug':
|
|
87
|
+
newState.slug = value || '';
|
|
88
|
+
break;
|
|
89
|
+
case 'scale':
|
|
90
|
+
newState.scale = value || '';
|
|
91
|
+
// Clear custom values when scale changes away from custom
|
|
92
|
+
if (value !== 'custom') {
|
|
93
|
+
newState.customValues = [];
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
case 'customValues':
|
|
97
|
+
newState.customValues = value || [];
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
(newState as any)[field] = value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return newState;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Add a new custom value to the state
|
|
108
|
+
*/
|
|
109
|
+
export function addCustomValue(
|
|
110
|
+
state: BeliefNodeState,
|
|
111
|
+
value: string
|
|
112
|
+
): BeliefNodeState {
|
|
113
|
+
if (!value.trim()) return state;
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
...state,
|
|
117
|
+
customValues: [...state.customValues, value.trim()],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Remove a custom value from the state
|
|
123
|
+
*/
|
|
124
|
+
export function removeCustomValue(
|
|
125
|
+
state: BeliefNodeState,
|
|
126
|
+
index: number
|
|
127
|
+
): BeliefNodeState {
|
|
128
|
+
return {
|
|
129
|
+
...state,
|
|
130
|
+
customValues: state.customValues.filter((_, i) => i !== index),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Update a specific custom value in the state
|
|
136
|
+
*/
|
|
137
|
+
export function updateCustomValue(
|
|
138
|
+
state: BeliefNodeState,
|
|
139
|
+
index: number,
|
|
140
|
+
value: string
|
|
141
|
+
): BeliefNodeState {
|
|
142
|
+
const newCustomValues = [...state.customValues];
|
|
143
|
+
newCustomValues[index] = value;
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
...state,
|
|
147
|
+
customValues: newCustomValues,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Scale options for the belief form
|
|
153
|
+
*/
|
|
154
|
+
export const SCALE_OPTIONS = [
|
|
155
|
+
{ value: 'likert', label: 'Likert Scale (1-5)' },
|
|
156
|
+
{ value: 'agreement', label: 'Agreement (Agree/Disagree)' },
|
|
157
|
+
{ value: 'interest', label: 'Interest (Interested/Not Interested)' },
|
|
158
|
+
{ value: 'yn', label: 'Yes/No' },
|
|
159
|
+
{ value: 'tf', label: 'True/False' },
|
|
160
|
+
{ value: 'custom', label: 'Custom Values' },
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get scale preview data for displaying scale options
|
|
165
|
+
*/
|
|
166
|
+
export function getScalePreview(scale: string) {
|
|
167
|
+
const scalePreviewData: {
|
|
168
|
+
[key: string]: Array<{ id: number; name: string; color: string }>;
|
|
169
|
+
} = {
|
|
170
|
+
likert: [
|
|
171
|
+
{ id: 1, name: 'Strongly agree', color: 'bg-teal-400' },
|
|
172
|
+
{ id: 2, name: 'Agree', color: 'bg-lime-400' },
|
|
173
|
+
{ id: 3, name: 'Neither agree nor disagree', color: 'bg-slate-200' },
|
|
174
|
+
{ id: 4, name: 'Disagree', color: 'bg-amber-400' },
|
|
175
|
+
{ id: 5, name: 'Strongly disagree', color: 'bg-red-400' },
|
|
176
|
+
],
|
|
177
|
+
agreement: [
|
|
178
|
+
{ id: 1, name: 'Agree', color: 'bg-lime-400' },
|
|
179
|
+
{ id: 2, name: 'Disagree', color: 'bg-amber-400' },
|
|
180
|
+
],
|
|
181
|
+
interest: [
|
|
182
|
+
{ id: 1, name: 'Interested', color: 'bg-lime-400' },
|
|
183
|
+
{ id: 2, name: 'Not Interested', color: 'bg-amber-400' },
|
|
184
|
+
],
|
|
185
|
+
yn: [
|
|
186
|
+
{ id: 1, name: 'Yes', color: 'bg-lime-400' },
|
|
187
|
+
{ id: 2, name: 'No', color: 'bg-amber-400' },
|
|
188
|
+
],
|
|
189
|
+
tf: [
|
|
190
|
+
{ id: 1, name: 'True', color: 'bg-lime-400' },
|
|
191
|
+
{ id: 2, name: 'False', color: 'bg-amber-400' },
|
|
192
|
+
],
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return scalePreviewData[scale] || null;
|
|
196
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { TractStackAPI } from '../api';
|
|
2
|
+
import { convertToLocalState, convertToBackendFormat } from './brandHelpers';
|
|
3
|
+
import type { BrandConfig, BrandConfigState } from '@/types/tractstack';
|
|
4
|
+
|
|
5
|
+
export async function saveBrandConfig(
|
|
6
|
+
tenantId: string,
|
|
7
|
+
brandConfig: BrandConfig
|
|
8
|
+
): Promise<BrandConfig> {
|
|
9
|
+
const api = new TractStackAPI(tenantId);
|
|
10
|
+
try {
|
|
11
|
+
const response = await api.put('/api/v1/config/brand', {
|
|
12
|
+
...brandConfig,
|
|
13
|
+
SITE_INIT: true,
|
|
14
|
+
});
|
|
15
|
+
if (!response.success) {
|
|
16
|
+
throw new Error(response.error || 'Failed to save brand configuration');
|
|
17
|
+
}
|
|
18
|
+
return response.data;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
// If it's a network error (backend down), redirect to maintenance
|
|
21
|
+
if (error instanceof TypeError && error.message.includes('fetch failed')) {
|
|
22
|
+
window.location.href = `/maint?from=${encodeURIComponent(window.location.pathname)}`;
|
|
23
|
+
throw error; // Still throw so caller knows something failed
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function getBrandConfig(tenantId: string): Promise<BrandConfig> {
|
|
30
|
+
const api = new TractStackAPI(tenantId);
|
|
31
|
+
try {
|
|
32
|
+
const response = await api.get('/api/v1/config/brand');
|
|
33
|
+
if (!response.success) {
|
|
34
|
+
// Check if it's a backend down scenario based on error message
|
|
35
|
+
if (
|
|
36
|
+
response.error &&
|
|
37
|
+
(response.error.includes('Network error') ||
|
|
38
|
+
response.error.includes('fetch failed'))
|
|
39
|
+
) {
|
|
40
|
+
// Return empty/default config when backend is down
|
|
41
|
+
return {
|
|
42
|
+
SITE_INIT: false,
|
|
43
|
+
WORDMARK_MODE: '',
|
|
44
|
+
BRAND_COLOURS: '',
|
|
45
|
+
OPEN_DEMO: false,
|
|
46
|
+
HOME_SLUG: 'hello',
|
|
47
|
+
TRACTSTACK_HOME_SLUG: 'HELLO',
|
|
48
|
+
THEME: 'Default',
|
|
49
|
+
SOCIALS: '',
|
|
50
|
+
LOGO: '',
|
|
51
|
+
WORDMARK: '',
|
|
52
|
+
OG: '',
|
|
53
|
+
OGLOGO: '',
|
|
54
|
+
FAVICON: '',
|
|
55
|
+
SITE_URL: '',
|
|
56
|
+
SLOGAN: '',
|
|
57
|
+
FOOTER: '',
|
|
58
|
+
OGTITLE: '',
|
|
59
|
+
OGAUTHOR: '',
|
|
60
|
+
OGDESC: '',
|
|
61
|
+
GTAG: '',
|
|
62
|
+
STYLES_VER: 1,
|
|
63
|
+
KNOWN_RESOURCES: {},
|
|
64
|
+
HAS_AAI: false,
|
|
65
|
+
} as BrandConfig;
|
|
66
|
+
}
|
|
67
|
+
throw new Error(response.error || 'Failed to get brand configuration');
|
|
68
|
+
}
|
|
69
|
+
return response.data;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// If it's a network error (backend down), return default config
|
|
72
|
+
if (error instanceof TypeError && error.message.includes('fetch failed')) {
|
|
73
|
+
return {
|
|
74
|
+
SITE_INIT: false,
|
|
75
|
+
WORDMARK_MODE: '',
|
|
76
|
+
BRAND_COLOURS: '',
|
|
77
|
+
OPEN_DEMO: false,
|
|
78
|
+
HOME_SLUG: 'hello',
|
|
79
|
+
TRACTSTACK_HOME_SLUG: 'HELLO',
|
|
80
|
+
THEME: 'Default',
|
|
81
|
+
SOCIALS: '',
|
|
82
|
+
LOGO: '',
|
|
83
|
+
WORDMARK: '',
|
|
84
|
+
OG: '',
|
|
85
|
+
OGLOGO: '',
|
|
86
|
+
FAVICON: '',
|
|
87
|
+
SITE_URL: '',
|
|
88
|
+
SLOGAN: '',
|
|
89
|
+
FOOTER: '',
|
|
90
|
+
OGTITLE: '',
|
|
91
|
+
OGAUTHOR: '',
|
|
92
|
+
OGDESC: '',
|
|
93
|
+
GTAG: '',
|
|
94
|
+
STYLES_VER: 1,
|
|
95
|
+
KNOWN_RESOURCES: {},
|
|
96
|
+
HAS_AAI: false,
|
|
97
|
+
} as BrandConfig;
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Handle complete brand config save workflow including state updates
|
|
105
|
+
*/
|
|
106
|
+
export async function saveBrandConfigWithStateUpdate(
|
|
107
|
+
tenantId: string,
|
|
108
|
+
currentState: BrandConfigState
|
|
109
|
+
): Promise<BrandConfigState> {
|
|
110
|
+
const backendFormat = convertToBackendFormat(currentState);
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
// Send COMPLETE config, not just changed fields
|
|
114
|
+
await saveBrandConfig(tenantId, backendFormat);
|
|
115
|
+
|
|
116
|
+
// Get the complete updated config from backend
|
|
117
|
+
const freshConfig = await getBrandConfig(tenantId);
|
|
118
|
+
|
|
119
|
+
// Convert updated config back to local state format
|
|
120
|
+
const newLocalState = convertToLocalState(freshConfig);
|
|
121
|
+
|
|
122
|
+
return newLocalState;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { FieldErrors } from '@/hooks/useFormState';
|
|
2
|
+
import { getThemeColors } from '@/constants/brandThemes';
|
|
3
|
+
import type { BrandConfig, BrandConfigState } from '@/types/tractstack';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert backend BrandConfig to local state format
|
|
7
|
+
* Splits CSV strings into arrays for easier client-side manipulation
|
|
8
|
+
* Provides safe defaults for undefined values to prevent controlled input warnings
|
|
9
|
+
*/
|
|
10
|
+
export function convertToLocalState(
|
|
11
|
+
brandConfig: BrandConfig
|
|
12
|
+
): BrandConfigState {
|
|
13
|
+
return {
|
|
14
|
+
siteInit: brandConfig.SITE_INIT ?? false,
|
|
15
|
+
wordmarkMode: brandConfig.WORDMARK_MODE ?? '',
|
|
16
|
+
brandColours: brandConfig.BRAND_COLOURS
|
|
17
|
+
? brandConfig.BRAND_COLOURS.split(',').map((color) => color.trim())
|
|
18
|
+
: getThemeColors('Default'), // Fallback to Default theme
|
|
19
|
+
openDemo: brandConfig.OPEN_DEMO ?? false,
|
|
20
|
+
homeSlug: brandConfig.HOME_SLUG ?? 'hello',
|
|
21
|
+
tractstackHomeSlug: brandConfig.TRACTSTACK_HOME_SLUG ?? 'HELLO',
|
|
22
|
+
theme: brandConfig.THEME ?? 'light-bold',
|
|
23
|
+
socials: brandConfig.SOCIALS
|
|
24
|
+
? brandConfig.SOCIALS.split(',').filter((social) => social.trim())
|
|
25
|
+
: [],
|
|
26
|
+
logo: brandConfig.LOGO ?? '',
|
|
27
|
+
wordmark: brandConfig.WORDMARK ?? '',
|
|
28
|
+
og: brandConfig.OG ?? '',
|
|
29
|
+
oglogo: brandConfig.OGLOGO ?? '',
|
|
30
|
+
favicon: brandConfig.FAVICON ?? '',
|
|
31
|
+
siteUrl: brandConfig.SITE_URL ?? '',
|
|
32
|
+
slogan: brandConfig.SLOGAN ?? '',
|
|
33
|
+
footer: brandConfig.FOOTER ?? '',
|
|
34
|
+
ogtitle: brandConfig.OGTITLE ?? '',
|
|
35
|
+
ogauthor: brandConfig.OGAUTHOR ?? '',
|
|
36
|
+
ogdesc: brandConfig.OGDESC ?? '',
|
|
37
|
+
gtag: brandConfig.GTAG ?? '',
|
|
38
|
+
stylesVer: brandConfig.STYLES_VER ?? 1,
|
|
39
|
+
knownResources: brandConfig.KNOWN_RESOURCES ?? {},
|
|
40
|
+
hasAAI: brandConfig.HAS_AAI ?? false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Convert local state back to backend BrandConfig format
|
|
46
|
+
* Joins arrays back into CSV/pipe-separated strings
|
|
47
|
+
*/
|
|
48
|
+
export function convertToBackendFormat(
|
|
49
|
+
localState: BrandConfigState
|
|
50
|
+
): BrandConfig {
|
|
51
|
+
return {
|
|
52
|
+
SITE_INIT: localState.siteInit,
|
|
53
|
+
WORDMARK_MODE: localState.wordmarkMode,
|
|
54
|
+
BRAND_COLOURS: localState.brandColours.join(','),
|
|
55
|
+
OPEN_DEMO: localState.openDemo,
|
|
56
|
+
STYLES_VER: localState.stylesVer,
|
|
57
|
+
HOME_SLUG: localState.homeSlug,
|
|
58
|
+
TRACTSTACK_HOME_SLUG: localState.tractstackHomeSlug,
|
|
59
|
+
THEME: localState.theme,
|
|
60
|
+
SOCIALS: localState.socials.join(','),
|
|
61
|
+
SITE_URL: localState.siteUrl,
|
|
62
|
+
SLOGAN: localState.slogan,
|
|
63
|
+
FOOTER: localState.footer,
|
|
64
|
+
OGTITLE: localState.ogtitle,
|
|
65
|
+
OGAUTHOR: localState.ogauthor,
|
|
66
|
+
OGDESC: localState.ogdesc,
|
|
67
|
+
GTAG: localState.gtag,
|
|
68
|
+
KNOWN_RESOURCES: localState.knownResources,
|
|
69
|
+
HAS_AAI: localState.hasAAI,
|
|
70
|
+
|
|
71
|
+
// ALWAYS send asset paths (current state)
|
|
72
|
+
LOGO: localState.logo,
|
|
73
|
+
WORDMARK: localState.wordmark,
|
|
74
|
+
OG: localState.og,
|
|
75
|
+
OGLOGO: localState.oglogo,
|
|
76
|
+
FAVICON: localState.favicon,
|
|
77
|
+
|
|
78
|
+
// Only send base64 when uploading
|
|
79
|
+
LOGO_BASE64: localState.logoBase64,
|
|
80
|
+
WORDMARK_BASE64: localState.wordmarkBase64,
|
|
81
|
+
OG_BASE64: localState.ogBase64,
|
|
82
|
+
OGLOGO_BASE64: localState.oglogoBase64,
|
|
83
|
+
FAVICON_BASE64: localState.faviconBase64,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Validation function for brand configuration
|
|
89
|
+
* Returns field-level error messages
|
|
90
|
+
*/
|
|
91
|
+
export function validateBrandConfig(state: BrandConfigState): FieldErrors {
|
|
92
|
+
const errors: FieldErrors = {};
|
|
93
|
+
|
|
94
|
+
// Validate required fields
|
|
95
|
+
if (!state.siteUrl?.trim()) {
|
|
96
|
+
errors.siteUrl = 'Site URL is required';
|
|
97
|
+
} else if (!isValidUrl(state.siteUrl)) {
|
|
98
|
+
errors.siteUrl = 'Please enter a valid URL';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!state.slogan?.trim()) {
|
|
102
|
+
errors.slogan = 'Site slogan is required';
|
|
103
|
+
}
|
|
104
|
+
if (!state.footer?.trim()) {
|
|
105
|
+
errors.footer = 'Site footer is required';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Validate brand colors (must have exactly 8)
|
|
109
|
+
if (!state.brandColours || state.brandColours.length !== 8) {
|
|
110
|
+
errors.brandColours = 'Must have exactly 8 brand colors';
|
|
111
|
+
} else {
|
|
112
|
+
// Validate each color is a valid hex
|
|
113
|
+
const invalidColors = state.brandColours.filter(
|
|
114
|
+
(color) => !isValidHexColor(color)
|
|
115
|
+
);
|
|
116
|
+
if (invalidColors.length > 0) {
|
|
117
|
+
errors.brandColours = `Invalid hex colors: ${invalidColors.join(', ')}`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Validate social links format
|
|
122
|
+
if (state.socials && state.socials.length > 0) {
|
|
123
|
+
const invalidSocials = state.socials.filter((social) => {
|
|
124
|
+
const parts = social.split('|');
|
|
125
|
+
return parts.length !== 2 || !parts[0].trim() || !isValidUrl(parts[1]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (invalidSocials.length > 0) {
|
|
129
|
+
errors.socials = 'Social links must be in format "platform|url"';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return errors;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Helper function to validate URLs
|
|
138
|
+
*/
|
|
139
|
+
function isValidUrl(url: string): boolean {
|
|
140
|
+
try {
|
|
141
|
+
new URL(url);
|
|
142
|
+
return true;
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Helper function to validate hex colors
|
|
150
|
+
* Accepts both 3 and 6 character hex codes (with or without #)
|
|
151
|
+
*/
|
|
152
|
+
function isValidHexColor(color: string): boolean {
|
|
153
|
+
const hex = color.startsWith('#') ? color.slice(1) : color;
|
|
154
|
+
return /^([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(hex);
|
|
155
|
+
}
|