@woodsportal/hubspot-kit 1.0.4 → 1.0.26
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/CHANGELOG.md +9 -0
- package/README.md +4 -2
- package/dist/{chunk-JDGIAYHX.js → chunk-GTTIJVAR.js} +43 -3
- package/dist/chunk-GTTIJVAR.js.map +1 -0
- package/dist/cli.js +1400 -338
- package/dist/cli.js.map +1 -1
- package/dist/exec-U2OJATR3.js +12 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/examples/greenfield/module-hybrid-cdn/README.md +40 -3
- package/examples/greenfield/module-hybrid-cdn/public/fields.json +44 -1
- package/examples/greenfield/module-hybrid-cdn/src/app.tsx +8 -0
- package/examples/greenfield/module-hybrid-cdn/src/assets/css/main.dev.css +6 -1
- package/examples/greenfield/module-hybrid-cdn/src/cdn/app-entry.ts +32 -0
- package/examples/greenfield/module-hybrid-cdn/src/cdn/vendor-entry.ts +28 -0
- package/examples/greenfield/module-hybrid-cdn/src/components/layout/section-card.tsx +20 -0
- package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-nav.tsx +28 -0
- package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-shell.tsx +18 -0
- package/examples/greenfield/module-hybrid-cdn/src/config/router.ts +4 -0
- package/examples/greenfield/module-hybrid-cdn/src/config/routes.ts +5 -0
- package/examples/greenfield/module-hybrid-cdn/src/defaultData.ts +2 -7
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-guide-page.tsx +93 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home-page.tsx +21 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +52 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-single-page.tsx +17 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +47 -0
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/index.ts +6 -0
- package/examples/greenfield/module-hybrid-cdn/src/hubspot/bootstrap.ts +10 -0
- package/examples/greenfield/module-hybrid-cdn/src/hubspot/default-data.ts +7 -0
- package/examples/greenfield/module-hybrid-cdn/src/integrations/tanstack-router/router.tsx +25 -0
- package/examples/greenfield/module-hybrid-cdn/src/main.tsx +4 -3
- package/examples/greenfield/module-hybrid-cdn/src/portal/mount-dev-preview.tsx +8 -0
- package/examples/greenfield/module-hybrid-cdn/src/portal/mount-portal.tsx +12 -0
- package/examples/greenfield/module-hybrid-cdn/src/routeTree.gen.ts +77 -0
- package/examples/greenfield/module-hybrid-cdn/src/routes/__root.tsx +15 -0
- package/examples/greenfield/module-hybrid-cdn/src/routes/guide.tsx +7 -0
- package/examples/greenfield/module-hybrid-cdn/src/routes/index.tsx +7 -0
- package/examples/greenfield/module-hybrid-cdn/tsconfig.json +20 -0
- package/examples/greenfield/module-monolith/public/fields.json +44 -1
- package/examples/greenfield/module-monolith/src/app.tsx +8 -0
- package/examples/greenfield/module-monolith/src/assets/css/main.dev.css +8 -0
- package/examples/greenfield/module-monolith/src/components/layout/section-card.tsx +20 -0
- package/examples/greenfield/module-monolith/src/components/layout/starter-nav.tsx +28 -0
- package/examples/greenfield/module-monolith/src/components/layout/starter-shell.tsx +18 -0
- package/examples/greenfield/module-monolith/src/config/router.ts +4 -0
- package/examples/greenfield/module-monolith/src/config/routes.ts +5 -0
- package/examples/greenfield/module-monolith/src/defaultData.ts +2 -7
- package/examples/greenfield/module-monolith/src/features/starter/components/starter-guide-page.tsx +93 -0
- package/examples/greenfield/module-monolith/src/features/starter/components/starter-home-page.tsx +21 -0
- package/examples/greenfield/module-monolith/src/features/starter/components/starter-home.tsx +52 -0
- package/examples/greenfield/module-monolith/src/features/starter/components/starter-single-page.tsx +17 -0
- package/examples/greenfield/module-monolith/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/examples/greenfield/module-monolith/src/features/starter/hooks/use-starter-preview.ts +47 -0
- package/examples/greenfield/module-monolith/src/features/starter/index.ts +6 -0
- package/examples/greenfield/module-monolith/src/hubspot/default-data.ts +7 -0
- package/examples/greenfield/module-monolith/src/hubspot/types.ts +17 -0
- package/examples/greenfield/module-monolith/src/integrations/tanstack-router/router.tsx +25 -0
- package/examples/greenfield/module-monolith/src/main.tsx +5 -0
- package/examples/greenfield/module-monolith/src/portal/mount-dev-preview.tsx +8 -0
- package/examples/greenfield/module-monolith/src/portal/mount-portal.tsx +12 -0
- package/examples/greenfield/module-monolith/src/routeTree.gen.ts +77 -0
- package/examples/greenfield/module-monolith/src/routes/__root.tsx +15 -0
- package/examples/greenfield/module-monolith/src/routes/guide.tsx +7 -0
- package/examples/greenfield/module-monolith/src/routes/index.tsx +7 -0
- package/examples/greenfield/module-monolith/tsconfig.json +20 -0
- package/package.json +4 -1
- package/scripts/check-hubspot-bundle-size.mjs +5 -2
- package/scripts/ensure-wphs-workspace.mjs +2 -1
- package/scripts/hubspot-module-postbuild.mjs +1 -1
- package/scripts/read-woodsportal-config.mjs +4 -1
- package/scripts/run-hubspot-module-postbuild.mjs +9 -4
- package/scripts/vite/bootstrap-shim-resolver.js +47 -0
- package/scripts/vite/ensure-dev-kit-shims.js +7 -1
- package/scripts/vite/react-dev-plugin-options.js +2 -1
- package/scripts/watch-hubspot.mjs +107 -17
- package/scripts/wphs-paths.mjs +1 -0
- package/scripts/write-audit-artifacts.mjs +2 -1
- package/src/dev/bootstrap-greenfield.tsx +59 -0
- package/src/dev/bootstrap.tsx +46 -0
- package/src/dev-module-config/__tests__/fixtures/fields.json +3014 -0
- package/src/dev-module-config/__tests__/fixtures/golden-module.fixture.ts +102 -0
- package/src/dev-module-config/__tests__/module-config-storage.test.ts +40 -0
- package/src/dev-module-config/__tests__/module-config-transform.test.ts +55 -0
- package/src/dev-module-config/__tests__/normalize-repeater-items.test.ts +46 -0
- package/src/dev-module-config/dev-module-display-name.ts +5 -0
- package/src/dev-module-config/runtime/module-config-bridge.ts +27 -0
- package/src/dev-module-config/runtime/module-config-runtime.ts +185 -0
- package/src/dev-module-config/schema/build-default-item.ts +7 -0
- package/src/dev-module-config/schema/deep-merge.ts +27 -0
- package/src/dev-module-config/schema/extract-defaults.ts +62 -0
- package/src/dev-module-config/schema/normalize-choices.ts +18 -0
- package/src/dev-module-config/schema/normalize-repeater-items.ts +31 -0
- package/src/dev-module-config/schema/parse-fields-json.ts +2 -0
- package/src/dev-module-config/schema/types.ts +75 -0
- package/src/dev-module-config/schema/validate-module-config.ts +81 -0
- package/src/dev-module-config/schema/visibility.ts +130 -0
- package/src/dev-module-config/storage/config-json.ts +46 -0
- package/src/dev-module-config/storage/workspace-store.ts +139 -0
- package/src/dev-module-config/transform/build-hubspot-data.ts +409 -0
- package/src/dev-module-config/ui/FieldRenderer.tsx +353 -0
- package/src/dev-module-config/ui/ModuleConfigAnimatedPresence.tsx +72 -0
- package/src/dev-module-config/ui/ModuleConfigCollapsible.tsx +43 -0
- package/src/dev-module-config/ui/ModuleConfigDockSideIcon.tsx +27 -0
- package/src/dev-module-config/ui/ModuleConfigOverlay.tsx +134 -0
- package/src/dev-module-config/ui/ModuleConfigPersistenceStatus.tsx +89 -0
- package/src/dev-module-config/ui/ModuleConfigShell.tsx +355 -0
- package/src/dev-module-config/ui/ModuleConfigThemeToggle.tsx +32 -0
- package/src/dev-module-config/ui/ModuleConfigUiContext.tsx +65 -0
- package/src/dev-module-config/ui/editors/DeveloperOptionsEditor.tsx +61 -0
- package/src/dev-module-config/ui/editors/MenuOptionsEditor.tsx +103 -0
- package/src/dev-module-config/ui/hubspot/HsColorField.tsx +106 -0
- package/src/dev-module-config/ui/hubspot/HsColorPickerPopover.tsx +210 -0
- package/src/dev-module-config/ui/hubspot/HsFieldLabel.tsx +24 -0
- package/src/dev-module-config/ui/hubspot/HsFieldWidgets.tsx +248 -0
- package/src/dev-module-config/ui/hubspot/HsImageField.tsx +112 -0
- package/src/dev-module-config/ui/hubspot/HsOccurrenceGroupEditor.tsx +243 -0
- package/src/dev-module-config/ui/hubspot/HsRepeaterList.tsx +178 -0
- package/src/dev-module-config/ui/hubspot/HsSectionHeader.tsx +20 -0
- package/src/dev-module-config/ui/hubspot/HsToggle.tsx +39 -0
- package/src/dev-module-config/ui/hubspot/HsToggleRow.tsx +30 -0
- package/src/dev-module-config/ui/hubspot/hs-color-palette.ts +11 -0
- package/src/dev-module-config/ui/hubspot/module-config-typography.ts +26 -0
- package/src/dev-module-config/ui/module-config-motion.css +134 -0
- package/src/dev-module-config/ui/module-config-motion.ts +28 -0
- package/src/dev-module-config/ui/module-config-overlay-side.ts +29 -0
- package/src/dev-module-config/ui/module-config-surfaces.ts +27 -0
- package/src/dev-module-config/ui/module-config-theme.css +489 -0
- package/src/dev-module-config/ui/module-config-toolbar-theme.ts +35 -0
- package/src/dev-module-config/ui/navigation/ModuleConfigNavContext.tsx +194 -0
- package/src/dev-module-config/ui/use-dev-module-config-live-revision.ts +22 -0
- package/src/dev-module-config/ui/use-hubspot-live-exports.ts +27 -0
- package/src/dev-module-config/ui/use-module-config.ts +124 -0
- package/src/routing/build-api-routes.ts +37 -0
- package/src/routing/route-format.ts +19 -0
- package/templates/module-hybrid-cdn/README.md +53 -3
- package/templates/module-hybrid-cdn/index.html +12 -1
- package/templates/module-hybrid-cdn/package.json +6 -2
- package/templates/module-hybrid-cdn/public/fields.json +44 -1
- package/templates/module-hybrid-cdn/src/app.tsx +8 -0
- package/templates/module-hybrid-cdn/src/assets/css/main.dev.css +6 -1
- package/templates/module-hybrid-cdn/src/cdn/app-entry.ts +32 -0
- package/templates/module-hybrid-cdn/src/cdn/vendor-entry.ts +28 -0
- package/templates/module-hybrid-cdn/src/components/layout/section-card.tsx +20 -0
- package/templates/module-hybrid-cdn/src/components/layout/starter-nav.tsx +28 -0
- package/templates/module-hybrid-cdn/src/components/layout/starter-shell.tsx +18 -0
- package/templates/module-hybrid-cdn/src/config/router.ts +4 -0
- package/templates/module-hybrid-cdn/src/config/routes.ts +5 -0
- package/templates/module-hybrid-cdn/src/defaultData.ts +2 -7
- package/templates/module-hybrid-cdn/src/features/starter/components/starter-guide-page.tsx +93 -0
- package/templates/module-hybrid-cdn/src/features/starter/components/starter-home-page.tsx +21 -0
- package/templates/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +52 -0
- package/templates/module-hybrid-cdn/src/features/starter/components/starter-single-page.tsx +17 -0
- package/templates/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/templates/module-hybrid-cdn/src/features/starter/hooks/starter-preview-values.ts +42 -0
- package/templates/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.dev.ts +17 -0
- package/templates/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +30 -0
- package/templates/module-hybrid-cdn/src/features/starter/index.ts +6 -0
- package/templates/module-hybrid-cdn/src/hubspot/bootstrap.ts +10 -0
- package/templates/module-hybrid-cdn/src/hubspot/default-data.ts +7 -0
- package/templates/module-hybrid-cdn/src/hubspot/module-entry.tsx +9 -0
- package/templates/module-hybrid-cdn/src/integrations/tanstack-router/router.tsx +25 -0
- package/templates/module-hybrid-cdn/src/main.tsx +4 -3
- package/templates/module-hybrid-cdn/src/portal/mount-dev-preview.tsx +8 -0
- package/templates/module-hybrid-cdn/src/portal/mount-portal.tsx +12 -0
- package/templates/module-hybrid-cdn/src/routeTree.gen.ts +77 -0
- package/templates/module-hybrid-cdn/src/routes/__root.tsx +15 -0
- package/templates/module-hybrid-cdn/src/routes/guide.tsx +7 -0
- package/templates/module-hybrid-cdn/src/routes/index.tsx +7 -0
- package/templates/module-hybrid-cdn/tsconfig.json +20 -0
- package/templates/module-hybrid-cdn/vite.config.monolith.mjs +8 -0
- package/templates/module-monolith/README.md +59 -0
- package/templates/module-monolith/index.html +12 -0
- package/templates/module-monolith/package.json +10 -3
- package/templates/module-monolith/public/fields.json +44 -1
- package/templates/module-monolith/public/module.html +1 -0
- package/templates/module-monolith/src/app.tsx +8 -0
- package/templates/module-monolith/src/assets/css/main.dev.css +6 -1
- package/templates/module-monolith/src/components/layout/section-card.tsx +20 -0
- package/templates/module-monolith/src/components/layout/starter-nav.tsx +28 -0
- package/templates/module-monolith/src/components/layout/starter-shell.tsx +18 -0
- package/templates/module-monolith/src/config/router.ts +4 -0
- package/templates/module-monolith/src/config/routes.ts +5 -0
- package/templates/module-monolith/src/defaultData.ts +2 -7
- package/templates/module-monolith/src/features/starter/components/starter-guide-page.tsx +93 -0
- package/templates/module-monolith/src/features/starter/components/starter-home-page.tsx +21 -0
- package/templates/module-monolith/src/features/starter/components/starter-home.tsx +52 -0
- package/templates/module-monolith/src/features/starter/components/starter-single-page.tsx +17 -0
- package/templates/module-monolith/src/features/starter/content/woodscli-guide-content.ts +86 -0
- package/templates/module-monolith/src/features/starter/hooks/starter-preview-values.ts +42 -0
- package/templates/module-monolith/src/features/starter/hooks/use-starter-preview.dev.ts +17 -0
- package/templates/module-monolith/src/features/starter/hooks/use-starter-preview.ts +30 -0
- package/templates/module-monolith/src/features/starter/index.ts +6 -0
- package/templates/module-monolith/src/hubspot/default-data.ts +7 -0
- package/templates/module-monolith/src/hubspot/module-entry.tsx +9 -0
- package/templates/module-monolith/src/integrations/tanstack-router/router.tsx +25 -0
- package/templates/module-monolith/src/main.tsx +4 -3
- package/templates/module-monolith/src/portal/mount-dev-preview.tsx +8 -0
- package/templates/module-monolith/src/portal/mount-portal.tsx +12 -0
- package/templates/module-monolith/src/routeTree.gen.ts +77 -0
- package/templates/module-monolith/src/routes/__root.tsx +15 -0
- package/templates/module-monolith/src/routes/guide.tsx +7 -0
- package/templates/module-monolith/src/routes/index.tsx +7 -0
- package/templates/module-monolith/tsconfig.json +20 -0
- package/templates/module-monolith/vite.config.monolith.mjs +8 -0
- package/templates/module-monolith/woodscli.json +1 -0
- package/templates/shared/.env.dev.example +5 -0
- package/templates/shared/.env.prod.example +5 -0
- package/templates/shared/.env.stg.example +3 -0
- package/templates/shared/portal.cdn.json.example +8 -0
- package/templates/theme-starter/package.json +5 -2
- package/vite/vite.config.dev.mjs +9 -1
- package/vite/vite.config.monolith.mjs +22 -17
- package/dist/chunk-JDGIAYHX.js.map +0 -1
- package/dist/exec-K5C6LZP4.js +0 -10
- /package/dist/{exec-K5C6LZP4.js.map → exec-U2OJATR3.js.map} +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { ModuleFieldValues } from '../../schema/types'
|
|
2
|
+
|
|
3
|
+
/** Minimal module values for transform golden tests (HubSpot-like portal theme). */
|
|
4
|
+
export const goldenModuleFixture: ModuleFieldValues = {
|
|
5
|
+
add_home_tab: true,
|
|
6
|
+
backend_server_url: 'https://api.example.test',
|
|
7
|
+
home: {
|
|
8
|
+
tab_name: 'Dashboard',
|
|
9
|
+
icon_svg_code: '<svg></svg>',
|
|
10
|
+
view: 'cards',
|
|
11
|
+
enable_dashboard_cards: true,
|
|
12
|
+
dashboard_cards: [{ card_title: 'Open deals', card_type: 'count' }],
|
|
13
|
+
add_right_sidebar: false,
|
|
14
|
+
tabs_data_type: {},
|
|
15
|
+
},
|
|
16
|
+
menu_options: {
|
|
17
|
+
hubspot_defined_object: true,
|
|
18
|
+
hubspot_custom_object: false,
|
|
19
|
+
objects: [
|
|
20
|
+
{
|
|
21
|
+
tab_name: 'Contacts',
|
|
22
|
+
list_object_name: 'Contacts',
|
|
23
|
+
object_type_id: '0-1',
|
|
24
|
+
icon_svg_code: '<svg></svg>',
|
|
25
|
+
show_iframe: false,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
hubspot_custom_objects: [],
|
|
29
|
+
},
|
|
30
|
+
other_options: {
|
|
31
|
+
show_company_name: true,
|
|
32
|
+
show_sidebar_cta: false,
|
|
33
|
+
welcome_message: 'Welcome',
|
|
34
|
+
company_options: {
|
|
35
|
+
company_name: 'Acme Corp',
|
|
36
|
+
logo: { src: 'https://cdn.example/logo.png', alt: 'Logo' },
|
|
37
|
+
small_logo: { src: 'https://cdn.example/small.png', alt: 'Small' },
|
|
38
|
+
auth_popup_form_logo: { src: '', alt: '' },
|
|
39
|
+
},
|
|
40
|
+
sidebar_cta_details: {},
|
|
41
|
+
create_an_account_link: {
|
|
42
|
+
url: { href: 'https://example.test/signup', type: 'EXTERNAL' },
|
|
43
|
+
},
|
|
44
|
+
show_create_an_account_link: true,
|
|
45
|
+
},
|
|
46
|
+
style: {
|
|
47
|
+
module_global_colors: {
|
|
48
|
+
primary_color: { color: '#274E13', opacity: 100 },
|
|
49
|
+
secondary_color: { color: '#45818E', opacity: 100 },
|
|
50
|
+
button_text_color: { color: '#FFFFFF', opacity: 100 },
|
|
51
|
+
},
|
|
52
|
+
home_tab_styles: {
|
|
53
|
+
home_tab_banner_text_color: { color: '#111111', opacity: 100 },
|
|
54
|
+
description_color: { color: '#333333', opacity: 100 },
|
|
55
|
+
button_styles: {
|
|
56
|
+
background_color: { color: '#274E13', opacity: 100 },
|
|
57
|
+
text_color: { color: '#FFFFFF', opacity: 100 },
|
|
58
|
+
},
|
|
59
|
+
banner_overlayer_color: { color: '#274E13', opacity: 50 },
|
|
60
|
+
user_card_svg_color: { color: '#45818E', opacity: 100 },
|
|
61
|
+
},
|
|
62
|
+
sidebar_styles: {
|
|
63
|
+
text_color: { color: '#222222', opacity: 100 },
|
|
64
|
+
sidebar_background_color: { color: '#FFFFFF', opacity: 100 },
|
|
65
|
+
},
|
|
66
|
+
list_page_styles: {
|
|
67
|
+
heading_color: { color: '#111111', opacity: 100 },
|
|
68
|
+
},
|
|
69
|
+
details_page_styles: {
|
|
70
|
+
text_color: { color: '#111111', opacity: 100 },
|
|
71
|
+
banner_background_options: {
|
|
72
|
+
banner_color_1: { color: '#274E13', opacity: 100 },
|
|
73
|
+
banner_color_2: { color: '#45818E', opacity: 100 },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
table_record_creation_button: {
|
|
77
|
+
background_color: { color: '#274E13', opacity: 100 },
|
|
78
|
+
text_color: { color: '#FFFFFF', opacity: 100 },
|
|
79
|
+
},
|
|
80
|
+
d_h_sidebar: {
|
|
81
|
+
background_color: { color: '#FFFFFF', opacity: 100 },
|
|
82
|
+
card_background_color: { color: '#F5F5F5', opacity: 100 },
|
|
83
|
+
text_color: { color: '#111111', opacity: 100 },
|
|
84
|
+
},
|
|
85
|
+
note_cards: {
|
|
86
|
+
hs_note_card_bg_color: { color: '#FFFFFF', opacity: 100 },
|
|
87
|
+
hs_note_ts_text_color: { color: '#666666', opacity: 100 },
|
|
88
|
+
wp_note_card_bg_color: { color: '#FFFFFF', opacity: 100 },
|
|
89
|
+
wp_note_ts_text_color: { color: '#666666', opacity: 100 },
|
|
90
|
+
},
|
|
91
|
+
sidebar_cta: {
|
|
92
|
+
background_color: { color: '#274E13', opacity: 100 },
|
|
93
|
+
text_color: { color: '#FFFFFF', opacity: 100 },
|
|
94
|
+
button_background_color: { color: '#45818E', opacity: 100 },
|
|
95
|
+
button_text_color: { color: '#FFFFFF', opacity: 100 },
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
developerMode: false,
|
|
99
|
+
developer_option: false,
|
|
100
|
+
developer_options: { temp_backend_url: '' },
|
|
101
|
+
sso_buttons_customize_options: {},
|
|
102
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { exportConfigJson, importConfigJson } from '../storage/config-json'
|
|
3
|
+
import { isFieldVisible } from '../schema/visibility'
|
|
4
|
+
import { validateModuleConfig } from '../schema/validate-module-config'
|
|
5
|
+
import type { HubSpotFieldDefinition, ModuleFieldValues } from '../schema/types'
|
|
6
|
+
|
|
7
|
+
describe('config-json', () => {
|
|
8
|
+
it('round-trips module values', () => {
|
|
9
|
+
const moduleValues: ModuleFieldValues = { style: { primary_color: { color: '#112233', opacity: 100 } } }
|
|
10
|
+
const raw = exportConfigJson(moduleValues)
|
|
11
|
+
expect(importConfigJson(raw)).toEqual(moduleValues)
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
describe('visibility hubspot_data_source default', () => {
|
|
16
|
+
it('hides fields gated on hubspot_data_source when control is absent', () => {
|
|
17
|
+
const field: HubSpotFieldDefinition = {
|
|
18
|
+
name: 'list_object_properties',
|
|
19
|
+
label: 'List object properties',
|
|
20
|
+
type: 'text',
|
|
21
|
+
visibility: {
|
|
22
|
+
controlling_field_path: 'hubspot_data_source',
|
|
23
|
+
controlling_value_regex: 'true',
|
|
24
|
+
operator: 'EQUAL',
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
expect(isFieldVisible(field, { moduleValues: {} })).toBe(false)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
describe('validateModuleConfig', () => {
|
|
33
|
+
it('reports missing required fields', () => {
|
|
34
|
+
const schema: Array<HubSpotFieldDefinition> = [
|
|
35
|
+
{ name: 'welcome_message', label: 'Welcome Message', type: 'text', required: true },
|
|
36
|
+
]
|
|
37
|
+
const errors = validateModuleConfig(schema, { welcome_message: '' })
|
|
38
|
+
expect(errors.some((error) => error.path === 'welcome_message')).toBe(true)
|
|
39
|
+
})
|
|
40
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fieldsSchema from './fixtures/fields.json'
|
|
2
|
+
import { buildDefaultsFromFields } from '../schema/extract-defaults'
|
|
3
|
+
import { buildCssVarsFromFields, buildHubSpotDataFromFields } from '../transform/build-hubspot-data'
|
|
4
|
+
import { normalizeFieldChoices } from '../schema/normalize-choices'
|
|
5
|
+
import { goldenModuleFixture } from './fixtures/golden-module.fixture'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
describe('normalizeFieldChoices', () => {
|
|
9
|
+
it('parses HubSpot tuple choices', () => {
|
|
10
|
+
const choices = normalizeFieldChoices({
|
|
11
|
+
name: 'object_type_id',
|
|
12
|
+
label: 'Object Type',
|
|
13
|
+
type: 'choice',
|
|
14
|
+
choices: [['0-5', 'Tickets (0-5)'], ['0-3', 'Deals (0-3)']],
|
|
15
|
+
})
|
|
16
|
+
expect(choices).toEqual([
|
|
17
|
+
{ value: '0-5', label: 'Tickets (0-5)' },
|
|
18
|
+
{ value: '0-3', label: 'Deals (0-3)' },
|
|
19
|
+
])
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('buildDefaultsFromFields', () => {
|
|
24
|
+
it('extracts primary and secondary color defaults from fields.json', () => {
|
|
25
|
+
const defaults = buildDefaultsFromFields(fieldsSchema as never)
|
|
26
|
+
const style = defaults.style as Record<string, unknown>
|
|
27
|
+
const globals = style.module_global_colors as Record<string, { color: string }>
|
|
28
|
+
|
|
29
|
+
expect(globals.primary_color.color).toBe('#FF7A59')
|
|
30
|
+
expect(globals.secondary_color.color).toBe('#2797AF')
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('buildHubSpotDataFromFields', () => {
|
|
35
|
+
it('maps module style colors to hubSpotData primary/secondary fields', () => {
|
|
36
|
+
const built = buildHubSpotDataFromFields(goldenModuleFixture)
|
|
37
|
+
|
|
38
|
+
expect(built.primarycolor).toBe('#274E13')
|
|
39
|
+
expect(built.secondarycolor).toBe('#45818E')
|
|
40
|
+
expect(built.addHomeTabOption).toBe(true)
|
|
41
|
+
expect(built.enableDashboardCards).toBe(true)
|
|
42
|
+
expect(built.sidebarMenuOptions.length).toBeGreaterThan(0)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe('buildCssVarsFromFields', () => {
|
|
47
|
+
it('sets :root primary and secondary CSS variables from module styles', () => {
|
|
48
|
+
const vars = buildCssVarsFromFields(goldenModuleFixture)
|
|
49
|
+
|
|
50
|
+
expect(vars['--primary-color']).toBe('#274E13')
|
|
51
|
+
expect(vars['--secondary-color']).toBe('#45818E')
|
|
52
|
+
expect(vars['--primary-color-hover-bg']).toBe('#274E1330')
|
|
53
|
+
expect(vars['--secondary-color-hover-bg']).toBe('#45818E10')
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { normalizeRepeaterItems } from '../schema/normalize-repeater-items'
|
|
2
|
+
import { buildHubSpotDataFromFields } from '../transform/build-hubspot-data'
|
|
3
|
+
import { goldenModuleFixture } from './fixtures/golden-module.fixture'
|
|
4
|
+
|
|
5
|
+
describe('normalizeRepeaterItems', () => {
|
|
6
|
+
it('returns arrays unchanged', () => {
|
|
7
|
+
const items = [{ list_object_name: 'Tickets' }, { list_object_name: 'Deals' }]
|
|
8
|
+
expect(normalizeRepeaterItems(items)).toEqual(items)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('wraps a single object row', () => {
|
|
12
|
+
const row = { list_object_name: 'Tickets', object_type_id: '0-5' }
|
|
13
|
+
expect(normalizeRepeaterItems(row)).toEqual([row])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('converts numeric-key objects to arrays', () => {
|
|
17
|
+
expect(
|
|
18
|
+
normalizeRepeaterItems({
|
|
19
|
+
0: { list_object_name: 'Tickets' },
|
|
20
|
+
1: { list_object_name: 'Deals' },
|
|
21
|
+
}),
|
|
22
|
+
).toEqual([{ list_object_name: 'Tickets' }, { list_object_name: 'Deals' }])
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('buildHubSpotDataFromFields right sidebar', () => {
|
|
27
|
+
it('maps enabled right sidebar objects from a single-object repeater value', () => {
|
|
28
|
+
const built = buildHubSpotDataFromFields({
|
|
29
|
+
...goldenModuleFixture,
|
|
30
|
+
home: {
|
|
31
|
+
...(goldenModuleFixture.home as Record<string, unknown>),
|
|
32
|
+
add_right_sidebar: true,
|
|
33
|
+
right_sidebar_object: {
|
|
34
|
+
list_object_name: 'Tickets',
|
|
35
|
+
object_type_id: '0-5',
|
|
36
|
+
show_iframe: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
expect(built.showSidebarListDataOption).toBe(true)
|
|
42
|
+
expect(built.sidebarListDataOption).toHaveLength(1)
|
|
43
|
+
expect(built.sidebarListDataOption[0]?.label).toBe('Tickets')
|
|
44
|
+
expect(built.sidebarListDataOption[0]?.hubspotObjectTypeId).toBe('0-5')
|
|
45
|
+
})
|
|
46
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { MODULE_CONFIG_CHANGED } from '../schema/types'
|
|
2
|
+
import type { ModuleConfigState } from './module-config-runtime'
|
|
3
|
+
import type { HubSpotKitAdapter } from '../../adapters/types.js'
|
|
4
|
+
|
|
5
|
+
let adapterRef: HubSpotKitAdapter | null = null
|
|
6
|
+
|
|
7
|
+
export function setModuleConfigAdapter(adapter: HubSpotKitAdapter): void {
|
|
8
|
+
adapterRef = adapter
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getAdapter(): HubSpotKitAdapter {
|
|
12
|
+
if (!adapterRef) {
|
|
13
|
+
throw new Error('Module config adapter not set. Import wphs.adapter.ts in bootstrap.')
|
|
14
|
+
}
|
|
15
|
+
return adapterRef
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function bindModuleConfigToHubSpotData(): void {
|
|
19
|
+
window.addEventListener(MODULE_CONFIG_CHANGED, (event) => {
|
|
20
|
+
const detail = (event as CustomEvent<ModuleConfigState>).detail
|
|
21
|
+
getAdapter().syncLiveData(detail.built)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function syncHubSpotDataFromModuleState(state: ModuleConfigState): void {
|
|
26
|
+
getAdapter().syncLiveData(state.built)
|
|
27
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { loadModuleDefaults } from '../schema/extract-defaults'
|
|
2
|
+
import { deepMerge } from '../schema/deep-merge'
|
|
3
|
+
import { MODULE_CONFIG_CHANGED } from '../schema/types'
|
|
4
|
+
import {
|
|
5
|
+
clearOverrides,
|
|
6
|
+
exportConfigJson,
|
|
7
|
+
importConfigJson,
|
|
8
|
+
loadOverrides,
|
|
9
|
+
saveOverrides,
|
|
10
|
+
} from '../storage/workspace-store'
|
|
11
|
+
import { buildApiRoutes } from '@woodsportal/hubspot-kit/routing/build-api-routes'
|
|
12
|
+
import {
|
|
13
|
+
applyCreateButtonStyles,
|
|
14
|
+
applyCssVarsToDocument,
|
|
15
|
+
buildCssVarsFromFields,
|
|
16
|
+
buildHubSpotDataFromFields,
|
|
17
|
+
} from '../transform/build-hubspot-data'
|
|
18
|
+
import type { BuiltHubSpotData } from '../transform/build-hubspot-data'
|
|
19
|
+
import type { HubSpotFieldDefinition, ModuleFieldValues } from '../schema/types'
|
|
20
|
+
import type { HubSpotPortalData } from '@/hubspot/types'
|
|
21
|
+
import { defaultData } from '@/defaultData'
|
|
22
|
+
|
|
23
|
+
export type ModuleConfigState = {
|
|
24
|
+
schema: Array<HubSpotFieldDefinition>
|
|
25
|
+
defaults: ModuleFieldValues
|
|
26
|
+
overrides: ModuleFieldValues
|
|
27
|
+
moduleValues: ModuleFieldValues
|
|
28
|
+
built: BuiltHubSpotData
|
|
29
|
+
apiRoutes: ReturnType<typeof buildApiRoutes>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let state: ModuleConfigState | null = null
|
|
33
|
+
let schemaCache: Array<HubSpotFieldDefinition> | null = null
|
|
34
|
+
|
|
35
|
+
async function loadSchema(): Promise<Array<HubSpotFieldDefinition>> {
|
|
36
|
+
if (schemaCache) return schemaCache
|
|
37
|
+
const response = await fetch('/fields.json')
|
|
38
|
+
if (!response.ok) throw new Error('Failed to load fields.json')
|
|
39
|
+
schemaCache = (await response.json()) as Array<HubSpotFieldDefinition>
|
|
40
|
+
return schemaCache
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildState(
|
|
44
|
+
schema: Array<HubSpotFieldDefinition>,
|
|
45
|
+
defaults: ModuleFieldValues,
|
|
46
|
+
overrides: ModuleFieldValues,
|
|
47
|
+
): ModuleConfigState {
|
|
48
|
+
const moduleValues = deepMerge(defaults, overrides)
|
|
49
|
+
const built = buildHubSpotDataFromFields(moduleValues)
|
|
50
|
+
const apiRoutes = buildApiRoutes(built.sidebarMenuOptions)
|
|
51
|
+
return { schema, defaults, overrides, moduleValues, built, apiRoutes }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function applyRuntimeSideEffects(state: ModuleConfigState): void {
|
|
55
|
+
applyCssVarsToDocument(buildCssVarsFromFields(state.moduleValues))
|
|
56
|
+
applyCreateButtonStyles(state.moduleValues)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function initModuleConfigRuntime(): Promise<ModuleConfigState> {
|
|
60
|
+
const [schema, defaults, overrides] = await Promise.all([
|
|
61
|
+
loadSchema(),
|
|
62
|
+
loadModuleDefaults(),
|
|
63
|
+
loadOverrides(),
|
|
64
|
+
])
|
|
65
|
+
state = buildState(schema, defaults, overrides)
|
|
66
|
+
applyRuntimeSideEffects(state)
|
|
67
|
+
return state
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function getModuleConfigState(): ModuleConfigState | null {
|
|
71
|
+
return state
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getBuiltHubSpotData(): BuiltHubSpotData | null {
|
|
75
|
+
return state?.built ?? null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getModuleFieldValues(): ModuleFieldValues {
|
|
79
|
+
return state?.moduleValues ?? {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function getModuleApiRoutes() {
|
|
83
|
+
return state?.apiRoutes ?? []
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function updateModuleFieldValues(
|
|
87
|
+
nextModuleValues: ModuleFieldValues,
|
|
88
|
+
): Promise<ModuleConfigState> {
|
|
89
|
+
if (!state) throw new Error('Module config runtime not initialized')
|
|
90
|
+
const overrides = deepMerge({}, nextModuleValues)
|
|
91
|
+
state = buildState(state.schema, state.defaults, overrides)
|
|
92
|
+
await saveOverrides(overrides)
|
|
93
|
+
applyRuntimeSideEffects(state)
|
|
94
|
+
window.dispatchEvent(new CustomEvent(MODULE_CONFIG_CHANGED, { detail: state }))
|
|
95
|
+
return state
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function resetModuleConfigToDefaults(): Promise<ModuleConfigState> {
|
|
99
|
+
if (!state) throw new Error('Module config runtime not initialized')
|
|
100
|
+
await clearOverrides()
|
|
101
|
+
state = buildState(state.schema, state.defaults, {})
|
|
102
|
+
applyRuntimeSideEffects(state)
|
|
103
|
+
window.dispatchEvent(new CustomEvent(MODULE_CONFIG_CHANGED, { detail: state }))
|
|
104
|
+
return state
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function exportModuleConfig(): Promise<string> {
|
|
108
|
+
const moduleValues = getModuleFieldValues()
|
|
109
|
+
return exportConfigJson(moduleValues)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function importModuleConfig(raw: string): Promise<ModuleConfigState> {
|
|
113
|
+
const moduleValues = importConfigJson(raw)
|
|
114
|
+
return updateModuleFieldValues(moduleValues)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Merge CMS module config with local-only auth fixtures from defaultData. */
|
|
118
|
+
export function buildFullDevHubSpotPortalData(): HubSpotPortalData {
|
|
119
|
+
const built = getBuiltHubSpotData()
|
|
120
|
+
if (!built) {
|
|
121
|
+
return {
|
|
122
|
+
dataSourceSet: defaultData.dataSourceSet,
|
|
123
|
+
hubId: defaultData.hubId,
|
|
124
|
+
userData: defaultData.userData,
|
|
125
|
+
objectList: defaultData.objectList,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
dataSourceSet: built.dataSourceSet,
|
|
131
|
+
hubId: defaultData.hubId,
|
|
132
|
+
apiBaseUrlOption: built.apiBaseUrlOption,
|
|
133
|
+
userData: defaultData.userData,
|
|
134
|
+
objectList: defaultData.objectList,
|
|
135
|
+
showCompanyNameOption: built.showCompanyNameOption,
|
|
136
|
+
showSidebarCtaOption: built.showSidebarCtaOption,
|
|
137
|
+
addHomeTabOption: built.addHomeTabOption,
|
|
138
|
+
sidebarMenuOptions: built.sidebarMenuOptions,
|
|
139
|
+
moduleIframeListOptions: built.moduleIframeListOptions,
|
|
140
|
+
sidebarListDataOption: built.sidebarListDataOption,
|
|
141
|
+
showSidebarListDataOption: built.showSidebarListDataOption,
|
|
142
|
+
sidebarCtaDetails: built.sidebarCtaDetails,
|
|
143
|
+
baseCompanyOptions: built.baseCompanyOptions,
|
|
144
|
+
enableDashboardCards: built.enableDashboardCards,
|
|
145
|
+
dashboardCards: built.dashboardCards,
|
|
146
|
+
homeTabsDataTypeFilter: built.homeTabsDataTypeFilter,
|
|
147
|
+
moduleStylesOptions: built.moduleStylesOptions,
|
|
148
|
+
recorBtnCustom: built.recorBtnCustom,
|
|
149
|
+
sidebarCtaStyles: built.sidebarCtaStyles,
|
|
150
|
+
developerModeModule: built.developerModeModule,
|
|
151
|
+
developerOptionTempUrl: built.developerOptionTempUrl,
|
|
152
|
+
developerOption: built.developerOption,
|
|
153
|
+
ssoButtonsCustomizeOptions: built.ssoButtonsCustomizeOptions,
|
|
154
|
+
isInEditor: false,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function setNestedValue(
|
|
159
|
+
obj: ModuleFieldValues,
|
|
160
|
+
path: string,
|
|
161
|
+
value: unknown,
|
|
162
|
+
): ModuleFieldValues {
|
|
163
|
+
const parts = path.split('.')
|
|
164
|
+
const result = structuredClone(obj)
|
|
165
|
+
let cursor: Record<string, unknown> = result
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
168
|
+
const key = parts[i]
|
|
169
|
+
const next = cursor[key]
|
|
170
|
+
if (next === null || typeof next !== 'object' || Array.isArray(next)) {
|
|
171
|
+
cursor[key] = {}
|
|
172
|
+
}
|
|
173
|
+
cursor = cursor[key] as Record<string, unknown>
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
cursor[parts[parts.length - 1]] = value
|
|
177
|
+
return result
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function getNestedValue(obj: ModuleFieldValues, path: string): unknown {
|
|
181
|
+
return path.split('.').reduce<unknown>((acc, key) => {
|
|
182
|
+
if (acc === null || acc === undefined || typeof acc !== 'object') return undefined
|
|
183
|
+
return (acc as Record<string, unknown>)[key]
|
|
184
|
+
}, obj)
|
|
185
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { buildDefaultsFromFields } from './extract-defaults'
|
|
2
|
+
import type { HubSpotFieldDefinition, ModuleFieldValues } from './types'
|
|
3
|
+
|
|
4
|
+
/** Build a single repeater row default from field children (uses fields.json defaults). */
|
|
5
|
+
export function buildDefaultItemFromChildren(children: Array<HubSpotFieldDefinition>): ModuleFieldValues {
|
|
6
|
+
return buildDefaultsFromFields(children)
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function deepMerge<T extends Record<string, unknown>>(
|
|
2
|
+
base: T,
|
|
3
|
+
patch: Record<string, unknown>,
|
|
4
|
+
): T {
|
|
5
|
+
const result = { ...base } as Record<string, unknown>
|
|
6
|
+
|
|
7
|
+
for (const [key, patchValue] of Object.entries(patch)) {
|
|
8
|
+
const baseValue = result[key]
|
|
9
|
+
if (
|
|
10
|
+
patchValue !== null &&
|
|
11
|
+
typeof patchValue === 'object' &&
|
|
12
|
+
!Array.isArray(patchValue) &&
|
|
13
|
+
baseValue !== null &&
|
|
14
|
+
typeof baseValue === 'object' &&
|
|
15
|
+
!Array.isArray(baseValue)
|
|
16
|
+
) {
|
|
17
|
+
result[key] = deepMerge(
|
|
18
|
+
baseValue as Record<string, unknown>,
|
|
19
|
+
patchValue as Record<string, unknown>,
|
|
20
|
+
)
|
|
21
|
+
} else {
|
|
22
|
+
result[key] = patchValue
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return result as T
|
|
27
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { HubSpotFieldDefinition, ModuleFieldValues } from './types'
|
|
2
|
+
|
|
3
|
+
function isFieldGroup(field: HubSpotFieldDefinition): boolean {
|
|
4
|
+
return field.type === 'group' && Array.isArray(field.children)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function defaultOccurrenceCount(field: HubSpotFieldDefinition): number {
|
|
8
|
+
const occurrence = field.occurrence
|
|
9
|
+
if (!occurrence) return 0
|
|
10
|
+
const raw = occurrence.default ?? occurrence.min ?? 0
|
|
11
|
+
const count = Number(raw)
|
|
12
|
+
return Number.isFinite(count) && count > 0 ? count : 0
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function defaultForField(field: HubSpotFieldDefinition): unknown {
|
|
16
|
+
if (field.default !== undefined) return structuredClone(field.default)
|
|
17
|
+
if (isFieldGroup(field)) {
|
|
18
|
+
const row = buildDefaultsFromFields(field.children ?? [])
|
|
19
|
+
if (field.occurrence) {
|
|
20
|
+
const count = defaultOccurrenceCount(field)
|
|
21
|
+
return Array.from({ length: count }, () => structuredClone(row))
|
|
22
|
+
}
|
|
23
|
+
return row
|
|
24
|
+
}
|
|
25
|
+
if (field.type === 'boolean') return false
|
|
26
|
+
if (field.type === 'number') return 0
|
|
27
|
+
if (field.type === 'color') return { color: '', opacity: 100 }
|
|
28
|
+
if (field.type === 'image') return { src: '', alt: '' }
|
|
29
|
+
if (field.type === 'url' || field.type === 'link') {
|
|
30
|
+
return {
|
|
31
|
+
url: { href: '', type: 'EXTERNAL' },
|
|
32
|
+
open_in_new_tab: false,
|
|
33
|
+
no_follow: false,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return ''
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function buildDefaultsFromFields(
|
|
40
|
+
fields: Array<HubSpotFieldDefinition>,
|
|
41
|
+
): ModuleFieldValues {
|
|
42
|
+
const result: ModuleFieldValues = {}
|
|
43
|
+
|
|
44
|
+
for (const field of fields) {
|
|
45
|
+
result[field.name] = defaultForField(field)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return result
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function loadFieldsSchema(): Promise<Array<HubSpotFieldDefinition>> {
|
|
52
|
+
const response = await fetch('/fields.json')
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
throw new Error(`Failed to load fields.json: ${response.status}`)
|
|
55
|
+
}
|
|
56
|
+
return (await response.json()) as Array<HubSpotFieldDefinition>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function loadModuleDefaults(): Promise<ModuleFieldValues> {
|
|
60
|
+
const schema = await loadFieldsSchema()
|
|
61
|
+
return buildDefaultsFromFields(schema)
|
|
62
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { HubSpotFieldDefinition } from './types'
|
|
2
|
+
|
|
3
|
+
export type NormalizedChoice = { value: string; label: string }
|
|
4
|
+
|
|
5
|
+
export function normalizeFieldChoices(field: HubSpotFieldDefinition): Array<NormalizedChoice> {
|
|
6
|
+
const raw = field.choices ?? []
|
|
7
|
+
return raw.map((entry) => {
|
|
8
|
+
if (Array.isArray(entry)) {
|
|
9
|
+
const [value, label] = entry
|
|
10
|
+
return { value: String(value), label: String(label ?? value) }
|
|
11
|
+
}
|
|
12
|
+
if (entry && typeof entry === 'object' && 'value' in entry) {
|
|
13
|
+
const choice = entry as { value: string; label?: string }
|
|
14
|
+
return { value: String(choice.value), label: String(choice.label ?? choice.value) }
|
|
15
|
+
}
|
|
16
|
+
return { value: String(entry), label: String(entry) }
|
|
17
|
+
})
|
|
18
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ModuleFieldValues } from './types'
|
|
2
|
+
|
|
3
|
+
/** HubSpot repeaters are arrays; legacy defaults or partial merges may store a single object. */
|
|
4
|
+
export function normalizeRepeaterItems(value: unknown): Array<ModuleFieldValues> {
|
|
5
|
+
if (Array.isArray(value)) {
|
|
6
|
+
return value.filter(
|
|
7
|
+
(item): item is ModuleFieldValues =>
|
|
8
|
+
item !== null && typeof item === 'object' && !Array.isArray(item),
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (value && typeof value === 'object') {
|
|
13
|
+
const record = value as Record<string, unknown>
|
|
14
|
+
const keys = Object.keys(record)
|
|
15
|
+
if (keys.length > 0 && keys.every((key) => /^\d+$/.test(key))) {
|
|
16
|
+
return keys
|
|
17
|
+
.sort((a, b) => Number(a) - Number(b))
|
|
18
|
+
.map((key) => record[key])
|
|
19
|
+
.filter(
|
|
20
|
+
(item): item is ModuleFieldValues =>
|
|
21
|
+
item !== null && typeof item === 'object' && !Array.isArray(item),
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (keys.length > 0) {
|
|
26
|
+
return [record as ModuleFieldValues]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return []
|
|
31
|
+
}
|