@akinon/projectzero 2.0.0-beta.20 → 2.0.0-beta.22
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 +14 -0
- package/app-template/CHANGELOG.md +170 -0
- package/app-template/next.config.mjs +0 -1
- package/app-template/package.json +31 -30
- package/app-template/src/app/[pz]/[...prettyurl]/page.tsx +2 -2
- package/app-template/src/app/[pz]/account/layout.tsx +2 -1
- package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/blog/[slug]/page.tsx +4 -2
- package/app-template/src/app/[pz]/category/[pk]/page.tsx +11 -1
- package/app-template/src/app/[pz]/group-product/[pk]/page.tsx +2 -2
- package/app-template/src/app/[pz]/layout.tsx +3 -1
- package/app-template/src/app/[pz]/list/page.tsx +11 -1
- package/app-template/src/app/[pz]/page.tsx +13 -35
- package/app-template/src/app/[pz]/pages/[slug]/page.tsx +19 -0
- package/app-template/src/app/[pz]/product/[pk]/page.tsx +2 -2
- package/app-template/src/app/api/barcode-search/route.ts +1 -1
- package/app-template/src/app/api/cache/route.ts +1 -1
- package/app-template/src/app/api/image-proxy/route.ts +1 -1
- package/app-template/src/app/api/logout/route.ts +1 -1
- package/app-template/src/app/api/product-categories/route.ts +1 -1
- package/app-template/src/app/api/similar-product-list/route.ts +1 -1
- package/app-template/src/app/api/similar-products/route.ts +1 -1
- package/app-template/src/app/api/virtual-try-on/route.ts +1 -1
- package/app-template/src/app/api/web-vitals/route.ts +1 -1
- package/app-template/src/components/quantity-selector.tsx +16 -4
- package/app-template/src/components/widget-content.tsx +3 -3
- package/app-template/src/routes/index.ts +6 -6
- package/app-template/src/utils/__tests__/theme-page-context.test.ts +145 -0
- package/app-template/src/utils/theme-page-context.ts +309 -0
- package/app-template/src/views/basket/basket-item.tsx +107 -691
- package/app-template/src/views/basket/index.ts +0 -2
- package/app-template/src/views/basket/summary.tsx +75 -496
- package/app-template/src/views/breadcrumb.tsx +38 -13
- package/app-template/src/views/category/category-header.tsx +66 -289
- package/app-template/src/views/category/category-info.tsx +24 -173
- package/app-template/src/views/category/filters/index.tsx +48 -208
- package/app-template/src/views/category/layout.tsx +5 -7
- package/app-template/src/views/checkout/index.tsx +0 -5
- package/app-template/src/views/checkout/steps/payment/index.tsx +2 -5
- package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +1 -72
- package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +40 -171
- package/app-template/src/views/checkout/steps/shipping/address-box.tsx +12 -74
- package/app-template/src/views/checkout/steps/shipping/addresses.tsx +45 -128
- package/app-template/src/views/checkout/steps/shipping/shipping-options.tsx +27 -232
- package/app-template/src/views/checkout/summary.tsx +29 -303
- package/app-template/src/views/footer.tsx +13 -415
- package/app-template/src/views/guest-login/index.tsx +1 -1
- package/app-template/src/views/header/action-menu.tsx +45 -277
- package/app-template/src/views/header/band.tsx +21 -6
- package/app-template/src/views/header/index.tsx +47 -109
- package/app-template/src/views/header/mini-basket.tsx +45 -820
- package/app-template/src/views/header/navbar.tsx +111 -178
- package/app-template/src/views/header/search/index.tsx +32 -71
- package/app-template/src/views/header/search/results.tsx +65 -127
- package/app-template/src/views/product/accordion-wrapper.tsx +43 -135
- package/app-template/src/views/product/index.ts +1 -1
- package/app-template/src/views/product/layout.tsx +7 -2
- package/app-template/src/views/product/misc-buttons.tsx +25 -339
- package/app-template/src/views/product/product-actions.tsx +8 -137
- package/app-template/src/views/product/product-info.tsx +31 -69
- package/app-template/src/views/product/product-share.tsx +8 -11
- package/app-template/src/views/product/slider.tsx +79 -117
- package/app-template/src/views/product-item/index.tsx +46 -119
- package/app-template/src/widgets/footer-social.tsx +16 -47
- package/app-template/src/widgets/footer-subscription/index.tsx +17 -183
- package/codemods/migrate-auth-v5/index.js +339 -0
- package/codemods/migrate-auth-v5/transform.js +86 -0
- package/dist/commands/plugins.js +23 -2
- package/package.json +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/pages/[slug]/page.tsx +0 -15
- package/app-template/src/views/basket/basket-summary-context.tsx +0 -560
- package/app-template/src/views/basket/designer-context.tsx +0 -617
- package/app-template/src/views/breadcrumb/breadcrumb-client.tsx +0 -190
- package/app-template/src/views/breadcrumb/breadcrumb-registrar.tsx +0 -286
- package/app-template/src/views/breadcrumb/constants.ts +0 -15
- package/app-template/src/views/breadcrumb/index.tsx +0 -127
- package/app-template/src/views/category/native-widget-context.tsx +0 -257
- package/app-template/src/views/category/product-list-registrar.tsx +0 -665
- package/app-template/src/views/checkout/checkout-address-registrar.tsx +0 -254
- package/app-template/src/views/checkout/checkout-buttons-registrar.tsx +0 -183
- package/app-template/src/views/checkout/checkout-delivery-method-registrar.tsx +0 -259
- package/app-template/src/views/checkout/checkout-payment-options-registrar.tsx +0 -253
- package/app-template/src/views/checkout/checkout-summary-registrar.tsx +0 -183
- package/app-template/src/views/checkout/constants.ts +0 -5
- package/app-template/src/views/checkout/steps/payment/options/masterpass-rest.tsx +0 -15
- package/app-template/src/views/checkout/steps/payment/options/saved-card.tsx +0 -18
- package/app-template/src/views/footer/footer-app-banner-context.tsx +0 -326
- package/app-template/src/views/footer/footer-bottom-context.tsx +0 -215
- package/app-template/src/views/footer/footer-bottom-wrapper.tsx +0 -74
- package/app-template/src/views/footer/footer-layout-constants.ts +0 -35
- package/app-template/src/views/footer/footer-layout-registrar.tsx +0 -342
- package/app-template/src/views/footer/footer-layout-switcher.tsx +0 -110
- package/app-template/src/views/footer/footer-menu-context.tsx +0 -211
- package/app-template/src/views/footer/footer-native-widgets.tsx +0 -60
- package/app-template/src/views/footer/footer-social-context.tsx +0 -254
- package/app-template/src/views/footer/footer-subscription-context.tsx +0 -210
- package/app-template/src/views/footer/footer-utils.ts +0 -43
- package/app-template/src/views/footer/footer-value-props-context.tsx +0 -326
- package/app-template/src/views/footer/logo-settings.ts +0 -183
- package/app-template/src/views/footer/native-widget-config.ts +0 -262
- package/app-template/src/views/footer/subscription-settings.ts +0 -122
- package/app-template/src/views/footer/use-footer-logo.ts +0 -162
- package/app-template/src/views/header/designer-context.tsx +0 -261
- package/app-template/src/views/header/header-announcement-registrar.tsx +0 -267
- package/app-template/src/views/header/header-client-wrapper.tsx +0 -496
- package/app-template/src/views/header/header-content.tsx +0 -1026
- package/app-template/src/views/header/header-currency-registrar.tsx +0 -348
- package/app-template/src/views/header/header-icons-context.tsx +0 -262
- package/app-template/src/views/header/header-language-registrar.tsx +0 -348
- package/app-template/src/views/header/header-layout-context.tsx +0 -143
- package/app-template/src/views/header/header-layout-registrar.tsx +0 -658
- package/app-template/src/views/header/header-logo-context.tsx +0 -228
- package/app-template/src/views/header/header-logo.tsx +0 -118
- package/app-template/src/views/header/header-mini-basket-context.tsx +0 -524
- package/app-template/src/views/header/header-search-registrar.tsx +0 -511
- package/app-template/src/views/header/header-text-slider-registrar.tsx +0 -382
- package/app-template/src/views/header/inline-search.tsx +0 -262
- package/app-template/src/views/header/navbar-menu-context.tsx +0 -219
- package/app-template/src/views/header/search/search-input.tsx +0 -61
- package/app-template/src/views/header/server-settings-parser.ts +0 -1105
- package/app-template/src/views/header/use-header-icons.ts +0 -241
- package/app-template/src/views/header/use-header-logo.ts +0 -213
- package/app-template/src/views/header/use-navbar-menu.ts +0 -179
- package/app-template/src/views/product/accordion-section.tsx +0 -61
- package/app-template/src/views/product/custom-button-group.tsx +0 -69
- package/app-template/src/views/product/favorites-button-section.tsx +0 -69
- package/app-template/src/views/product/find-in-store-section.tsx +0 -60
- package/app-template/src/views/product/product-info-section.tsx +0 -140
- package/app-template/src/views/product/quantity-section.tsx +0 -73
- package/app-template/src/views/product/sale-tag.tsx +0 -10
- package/app-template/src/views/product/share-section.tsx +0 -357
- package/app-template/src/views/product/variants-section.tsx +0 -126
- package/app-template/src/views/product-detail/constants.ts +0 -272
- package/app-template/src/views/product-detail/index.ts +0 -10
- package/app-template/src/views/product-detail/product-detail-registrar.tsx +0 -616
- package/app-template/src/widgets/footer-app-banner.tsx +0 -444
- package/app-template/src/widgets/footer-bottom.tsx +0 -127
- package/app-template/src/widgets/footer-menu-compact.tsx +0 -238
- package/app-template/src/widgets/footer-menu-two.tsx +0 -298
- package/app-template/src/widgets/footer-social-client.tsx +0 -251
- package/app-template/src/widgets/footer-value-props.tsx +0 -201
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* jscodeshift transform: client-side next-auth v4 -> v5 migration.
|
|
3
|
+
*
|
|
4
|
+
* Converts:
|
|
5
|
+
* import { getServerSession } from 'next-auth/next';
|
|
6
|
+
* const session = await getServerSession();
|
|
7
|
+
*
|
|
8
|
+
* Into:
|
|
9
|
+
* import { auth } from 'auth';
|
|
10
|
+
* const session = await auth();
|
|
11
|
+
*
|
|
12
|
+
* Arguments passed to `getServerSession(authOptions, ...)` are dropped,
|
|
13
|
+
* since `auth()` needs none when using the root `src/auth.ts` config.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const AUTH_IMPORT_SOURCES = ['next-auth/next', 'next-auth'];
|
|
17
|
+
|
|
18
|
+
function transform(fileInfo, api) {
|
|
19
|
+
const j = api.jscodeshift;
|
|
20
|
+
const root = j(fileInfo.source);
|
|
21
|
+
let changed = false;
|
|
22
|
+
|
|
23
|
+
AUTH_IMPORT_SOURCES.forEach((sourceValue) => {
|
|
24
|
+
const imports = root.find(j.ImportDeclaration, {
|
|
25
|
+
source: { value: sourceValue }
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
imports.forEach((pathNode) => {
|
|
29
|
+
const specifiers = pathNode.node.specifiers || [];
|
|
30
|
+
const getServerSessionSpec = specifiers.find(
|
|
31
|
+
(s) =>
|
|
32
|
+
s.type === 'ImportSpecifier' &&
|
|
33
|
+
s.imported &&
|
|
34
|
+
s.imported.name === 'getServerSession'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (!getServerSessionSpec) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const localName = getServerSessionSpec.local
|
|
42
|
+
? getServerSessionSpec.local.name
|
|
43
|
+
: 'getServerSession';
|
|
44
|
+
|
|
45
|
+
const otherSpecs = specifiers.filter((s) => s !== getServerSessionSpec);
|
|
46
|
+
|
|
47
|
+
if (otherSpecs.length === 0) {
|
|
48
|
+
pathNode.node.specifiers = [
|
|
49
|
+
j.importSpecifier(j.identifier('auth'))
|
|
50
|
+
];
|
|
51
|
+
pathNode.node.source = j.literal('auth');
|
|
52
|
+
} else {
|
|
53
|
+
pathNode.node.specifiers = otherSpecs;
|
|
54
|
+
const authImport = j.importDeclaration(
|
|
55
|
+
[j.importSpecifier(j.identifier('auth'))],
|
|
56
|
+
j.literal('auth')
|
|
57
|
+
);
|
|
58
|
+
pathNode.insertAfter(authImport);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
root
|
|
62
|
+
.find(j.CallExpression, { callee: { name: localName } })
|
|
63
|
+
.forEach((callPath) => {
|
|
64
|
+
callPath.node.callee = j.identifier('auth');
|
|
65
|
+
callPath.node.arguments = [];
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
changed = true;
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
AUTH_IMPORT_SOURCES.forEach((sourceValue) => {
|
|
73
|
+
root
|
|
74
|
+
.find(j.ImportDeclaration, { source: { value: sourceValue } })
|
|
75
|
+
.forEach((pathNode) => {
|
|
76
|
+
if (!pathNode.node.specifiers || pathNode.node.specifiers.length === 0) {
|
|
77
|
+
j(pathNode).remove();
|
|
78
|
+
changed = true;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return changed ? root.toSource({ quote: 'single' }) : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = transform;
|
package/dist/commands/plugins.js
CHANGED
|
@@ -60,7 +60,10 @@ function checkVersion(pkg) {
|
|
|
60
60
|
const pkgInfo = (yield response.json());
|
|
61
61
|
const latestVersion = pkgInfo['dist-tags'].latest;
|
|
62
62
|
if (!semver_1.default.satisfies(pkg.dependencies['@akinon/next'], latestVersion)) {
|
|
63
|
-
console.warn(`\x1b[
|
|
63
|
+
console.warn(`\x1b[43m Warning: The "${packageName}" package is currently at`, `\x1b[41m version ${pkg.dependencies['@akinon/next']}`, `\x1b[43m Please upgrade it to the latest version (${latestVersion}) to ensure plugin compatibility.`, '\x1b[0m\n');
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log(`\x1b[42m Info: The package "${packageName}" is currently in the current version (${latestVersion}).`, '\x1b[0m\n');
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
catch (error) {
|
|
@@ -82,7 +85,25 @@ exports.default = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
82
85
|
throw new Error('plugins.js was not found in either of the expected locations.');
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
|
-
|
|
88
|
+
function findPackageJson() {
|
|
89
|
+
const packageJsonPaths = [
|
|
90
|
+
path_1.default.resolve(rootDir, './package.json'),
|
|
91
|
+
path_1.default.resolve(rootDir, './apps/projectzeronext/package.json')
|
|
92
|
+
];
|
|
93
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
94
|
+
try {
|
|
95
|
+
const pkg = require(packageJsonPath);
|
|
96
|
+
if (pkg.dependencies['@akinon/next']) {
|
|
97
|
+
return pkg;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
throw new Error('Could not find package.json with @akinon/next dependency');
|
|
105
|
+
}
|
|
106
|
+
const pkg = findPackageJson();
|
|
86
107
|
yield checkVersion(pkg);
|
|
87
108
|
const pluginsFilePath = findPluginsFilePath();
|
|
88
109
|
let installedPlugins = [];
|
package/package.json
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import ThemePlaceholder from '@akinon/next/components/theme-editor/theme-placeholder';
|
|
2
|
-
import { withSegmentDefaults } from '@akinon/next/hocs/server';
|
|
3
|
-
import { PageProps } from '@akinon/next/types';
|
|
4
|
-
|
|
5
|
-
export const dynamic = 'force-dynamic';
|
|
6
|
-
|
|
7
|
-
async function Page({ params }: PageProps<{ slug: string }>) {
|
|
8
|
-
return (
|
|
9
|
-
<div className="min-h-screen">
|
|
10
|
-
<ThemePlaceholder slug={`page-${params.slug}`} />
|
|
11
|
-
</div>
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default withSegmentDefaults(Page, { segmentType: 'page' });
|
|
@@ -1,560 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Basket Summary Designer Context
|
|
5
|
-
*
|
|
6
|
-
* Enables basket summary to be customized via Theme Editor.
|
|
7
|
-
* Uses the native widget pattern for live editing.
|
|
8
|
-
*
|
|
9
|
-
* @see .github/instructions/native-widget.instructions.md for implementation guide
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
createContext,
|
|
14
|
-
PropsWithChildren,
|
|
15
|
-
useCallback,
|
|
16
|
-
useContext,
|
|
17
|
-
useEffect,
|
|
18
|
-
useMemo,
|
|
19
|
-
useRef,
|
|
20
|
-
useState
|
|
21
|
-
} from 'react';
|
|
22
|
-
import { useExternalDesigner } from '@akinon/next/components/theme-editor/hooks/use-external-designer';
|
|
23
|
-
import { useNativeWidgetData } from '@akinon/next/components/theme-editor/hooks/use-native-widget-data';
|
|
24
|
-
|
|
25
|
-
// Constants
|
|
26
|
-
export const BASKET_SUMMARY_PLACEHOLDER_ID = 'basket-page';
|
|
27
|
-
export const BASKET_SUMMARY_SECTION_ID = 'basket-summary-section';
|
|
28
|
-
export const BASKET_SUMMARY_WIDGET_SLUG = 'basket-summary-styles';
|
|
29
|
-
|
|
30
|
-
// Section-level default styles
|
|
31
|
-
export const BASKET_SUMMARY_SECTION_STYLES = {
|
|
32
|
-
'background-color': '#f9fafb',
|
|
33
|
-
padding: '24px'
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// Block definitions for basket summary elements
|
|
37
|
-
export const BASKET_SUMMARY_BLOCKS = {
|
|
38
|
-
TITLE: {
|
|
39
|
-
id: 'basket-summary-title',
|
|
40
|
-
type: 'text',
|
|
41
|
-
label: 'Summary Title',
|
|
42
|
-
properties: {},
|
|
43
|
-
styles: {}
|
|
44
|
-
},
|
|
45
|
-
VOUCHER_SECTION: {
|
|
46
|
-
id: 'basket-summary-voucher-section',
|
|
47
|
-
type: 'group',
|
|
48
|
-
label: 'Voucher Section',
|
|
49
|
-
properties: {},
|
|
50
|
-
styles: {}
|
|
51
|
-
},
|
|
52
|
-
VOUCHER_TITLE: {
|
|
53
|
-
id: 'basket-summary-voucher-title',
|
|
54
|
-
type: 'text',
|
|
55
|
-
label: 'Voucher Section Title',
|
|
56
|
-
properties: {},
|
|
57
|
-
styles: {}
|
|
58
|
-
},
|
|
59
|
-
VOUCHER_FORM: {
|
|
60
|
-
id: 'basket-summary-voucher-form',
|
|
61
|
-
type: 'group',
|
|
62
|
-
label: 'Voucher Form',
|
|
63
|
-
properties: {},
|
|
64
|
-
styles: {}
|
|
65
|
-
},
|
|
66
|
-
VOUCHER_INPUT: {
|
|
67
|
-
id: 'basket-summary-voucher-input',
|
|
68
|
-
type: 'text',
|
|
69
|
-
label: 'Voucher Input',
|
|
70
|
-
properties: {},
|
|
71
|
-
styles: {}
|
|
72
|
-
},
|
|
73
|
-
VOUCHER_BUTTON: {
|
|
74
|
-
id: 'basket-summary-voucher-button',
|
|
75
|
-
type: 'button',
|
|
76
|
-
label: 'Voucher Button',
|
|
77
|
-
properties: {},
|
|
78
|
-
styles: {}
|
|
79
|
-
},
|
|
80
|
-
PRICE_SECTION: {
|
|
81
|
-
id: 'basket-summary-price-section',
|
|
82
|
-
type: 'group',
|
|
83
|
-
label: 'Price Section',
|
|
84
|
-
properties: {},
|
|
85
|
-
styles: {}
|
|
86
|
-
},
|
|
87
|
-
SUBTOTAL_ROW: {
|
|
88
|
-
id: 'basket-summary-subtotal-row',
|
|
89
|
-
type: 'group',
|
|
90
|
-
label: 'Subtotal Row',
|
|
91
|
-
properties: {},
|
|
92
|
-
styles: {}
|
|
93
|
-
},
|
|
94
|
-
SUBTOTAL_LABEL: {
|
|
95
|
-
id: 'basket-summary-subtotal-label',
|
|
96
|
-
type: 'text',
|
|
97
|
-
label: 'Subtotal Label',
|
|
98
|
-
properties: {},
|
|
99
|
-
styles: {}
|
|
100
|
-
},
|
|
101
|
-
SUBTOTAL_PRICE: {
|
|
102
|
-
id: 'basket-summary-subtotal-price',
|
|
103
|
-
type: 'text',
|
|
104
|
-
label: 'Subtotal Price',
|
|
105
|
-
properties: {},
|
|
106
|
-
styles: {}
|
|
107
|
-
},
|
|
108
|
-
DISCOUNT_ITEM_ROW: {
|
|
109
|
-
id: 'basket-summary-discount-item-row',
|
|
110
|
-
type: 'group',
|
|
111
|
-
label: 'Discount Item Row',
|
|
112
|
-
properties: {},
|
|
113
|
-
styles: {}
|
|
114
|
-
},
|
|
115
|
-
DISCOUNT_ITEM_LABEL: {
|
|
116
|
-
id: 'basket-summary-discount-item-label',
|
|
117
|
-
type: 'text',
|
|
118
|
-
label: 'Discount Item Label',
|
|
119
|
-
properties: {},
|
|
120
|
-
styles: {}
|
|
121
|
-
},
|
|
122
|
-
DISCOUNT_ITEM_PRICE: {
|
|
123
|
-
id: 'basket-summary-discount-item-price',
|
|
124
|
-
type: 'text',
|
|
125
|
-
label: 'Discount Item Price',
|
|
126
|
-
properties: {},
|
|
127
|
-
styles: {}
|
|
128
|
-
},
|
|
129
|
-
DISCOUNTS_TOTAL_LABEL: {
|
|
130
|
-
id: 'basket-summary-discounts-total-label',
|
|
131
|
-
type: 'text',
|
|
132
|
-
label: 'Discounts Total Label',
|
|
133
|
-
properties: {},
|
|
134
|
-
styles: {}
|
|
135
|
-
},
|
|
136
|
-
DISCOUNTS_TOTAL_PRICE: {
|
|
137
|
-
id: 'basket-summary-discounts-total-price',
|
|
138
|
-
type: 'text',
|
|
139
|
-
label: 'Discounts Total Price',
|
|
140
|
-
properties: {},
|
|
141
|
-
styles: {}
|
|
142
|
-
},
|
|
143
|
-
DISCOUNTS_TOTAL_ROW: {
|
|
144
|
-
id: 'basket-summary-discounts-total-row',
|
|
145
|
-
type: 'group',
|
|
146
|
-
label: 'Discounts Total Row',
|
|
147
|
-
properties: {},
|
|
148
|
-
styles: {}
|
|
149
|
-
},
|
|
150
|
-
TOTAL_ROW: {
|
|
151
|
-
id: 'basket-summary-total-row',
|
|
152
|
-
type: 'group',
|
|
153
|
-
label: 'Total Row',
|
|
154
|
-
properties: {},
|
|
155
|
-
styles: {}
|
|
156
|
-
},
|
|
157
|
-
TOTAL_LABEL: {
|
|
158
|
-
id: 'basket-summary-total-label',
|
|
159
|
-
type: 'text',
|
|
160
|
-
label: 'Total Label',
|
|
161
|
-
properties: {},
|
|
162
|
-
styles: {}
|
|
163
|
-
},
|
|
164
|
-
TOTAL_PRICE: {
|
|
165
|
-
id: 'basket-summary-total-price',
|
|
166
|
-
type: 'text',
|
|
167
|
-
label: 'Total Price',
|
|
168
|
-
properties: {},
|
|
169
|
-
styles: {}
|
|
170
|
-
},
|
|
171
|
-
CHECKOUT_SECTION: {
|
|
172
|
-
id: 'basket-summary-checkout-section',
|
|
173
|
-
type: 'group',
|
|
174
|
-
label: 'Checkout Section',
|
|
175
|
-
properties: {},
|
|
176
|
-
styles: {}
|
|
177
|
-
},
|
|
178
|
-
CHECKOUT_BUTTON: {
|
|
179
|
-
id: 'basket-summary-checkout-button',
|
|
180
|
-
type: 'button',
|
|
181
|
-
label: 'Checkout Button',
|
|
182
|
-
properties: {},
|
|
183
|
-
styles: {}
|
|
184
|
-
}
|
|
185
|
-
} as const;
|
|
186
|
-
|
|
187
|
-
interface ThemeBlock {
|
|
188
|
-
id: string;
|
|
189
|
-
type: string;
|
|
190
|
-
label: string;
|
|
191
|
-
styles?: Record<string, unknown>;
|
|
192
|
-
properties?: Record<string, unknown>;
|
|
193
|
-
blocks?: ThemeBlock[];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
interface ThemeSection {
|
|
197
|
-
id: string;
|
|
198
|
-
blocks: ThemeBlock[];
|
|
199
|
-
styles?: Record<string, unknown>;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
interface ThemePlaceholder {
|
|
203
|
-
id: string;
|
|
204
|
-
sections: ThemeSection[];
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
interface BasketSummaryContextValue {
|
|
208
|
-
isDesigner: boolean;
|
|
209
|
-
isSectionSelected: boolean;
|
|
210
|
-
selectedBlockId: string;
|
|
211
|
-
getSectionStyles: () => Record<string, unknown> | undefined;
|
|
212
|
-
getBlockData: (blockId: string) => ThemeBlock | undefined;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const BasketSummaryContext = createContext<BasketSummaryContextValue>({
|
|
216
|
-
isDesigner: false,
|
|
217
|
-
isSectionSelected: false,
|
|
218
|
-
selectedBlockId: '',
|
|
219
|
-
getSectionStyles: () => undefined,
|
|
220
|
-
getBlockData: () => undefined
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
export const useBasketSummary = () => useContext(BasketSummaryContext);
|
|
224
|
-
|
|
225
|
-
export const BasketSummaryProvider = ({ children }: PropsWithChildren) => {
|
|
226
|
-
const designerState = useExternalDesigner({
|
|
227
|
-
placeholderId: BASKET_SUMMARY_PLACEHOLDER_ID
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
const [isSectionSelected, setIsSectionSelected] = useState(false);
|
|
231
|
-
const [selectedBlockId, setSelectedBlockId] = useState('');
|
|
232
|
-
const [themeBlocks, setThemeBlocks] = useState<Map<string, ThemeBlock>>(
|
|
233
|
-
new Map()
|
|
234
|
-
);
|
|
235
|
-
const [sectionStyles, setSectionStyles] = useState<
|
|
236
|
-
Record<string, unknown> | undefined
|
|
237
|
-
>(BASKET_SUMMARY_SECTION_STYLES);
|
|
238
|
-
|
|
239
|
-
const themeBlocksRef = useRef<Map<string, ThemeBlock>>(new Map());
|
|
240
|
-
const hasRegisteredNativeWidget = useRef(false);
|
|
241
|
-
const hasReceivedThemeStyles = useRef(false);
|
|
242
|
-
|
|
243
|
-
// Flatten block meta for useNativeWidgetData
|
|
244
|
-
const blockMeta = useMemo(() => {
|
|
245
|
-
return Object.values(BASKET_SUMMARY_BLOCKS).map((block) => ({
|
|
246
|
-
id: block.id,
|
|
247
|
-
type: block.type,
|
|
248
|
-
label: block.label
|
|
249
|
-
}));
|
|
250
|
-
}, []);
|
|
251
|
-
|
|
252
|
-
// Load saved widget data (styles from backend)
|
|
253
|
-
const widgetData = useNativeWidgetData({
|
|
254
|
-
widgetSlug: BASKET_SUMMARY_WIDGET_SLUG,
|
|
255
|
-
sectionId: BASKET_SUMMARY_SECTION_ID,
|
|
256
|
-
blockMeta
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
const createBlock = useCallback((blockId: string): ThemeBlock => {
|
|
260
|
-
const blockDef = Object.values(BASKET_SUMMARY_BLOCKS).find(
|
|
261
|
-
(block) => block.id === blockId
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
if (!blockDef) {
|
|
265
|
-
return {
|
|
266
|
-
id: blockId,
|
|
267
|
-
type: 'text',
|
|
268
|
-
label: blockId,
|
|
269
|
-
properties: {},
|
|
270
|
-
styles: {},
|
|
271
|
-
blocks: []
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const existingBlock = themeBlocksRef.current.get(blockId);
|
|
276
|
-
|
|
277
|
-
return {
|
|
278
|
-
id: blockDef.id,
|
|
279
|
-
type: blockDef.type,
|
|
280
|
-
label: blockDef.label,
|
|
281
|
-
properties: existingBlock?.properties || blockDef.properties || {},
|
|
282
|
-
styles: existingBlock?.styles || blockDef.styles || {},
|
|
283
|
-
blocks: existingBlock?.blocks || []
|
|
284
|
-
};
|
|
285
|
-
}, []);
|
|
286
|
-
|
|
287
|
-
// Get default blocks with current styles
|
|
288
|
-
const getDefaultBlocks = useCallback(() => {
|
|
289
|
-
const voucherSection = createBlock(
|
|
290
|
-
BASKET_SUMMARY_BLOCKS.VOUCHER_SECTION.id
|
|
291
|
-
);
|
|
292
|
-
const voucherForm = createBlock(BASKET_SUMMARY_BLOCKS.VOUCHER_FORM.id);
|
|
293
|
-
const priceSection = createBlock(BASKET_SUMMARY_BLOCKS.PRICE_SECTION.id);
|
|
294
|
-
const subtotalRow = createBlock(BASKET_SUMMARY_BLOCKS.SUBTOTAL_ROW.id);
|
|
295
|
-
const discountItemRow = createBlock(
|
|
296
|
-
BASKET_SUMMARY_BLOCKS.DISCOUNT_ITEM_ROW.id
|
|
297
|
-
);
|
|
298
|
-
const discountsTotalRow = createBlock(
|
|
299
|
-
BASKET_SUMMARY_BLOCKS.DISCOUNTS_TOTAL_ROW.id
|
|
300
|
-
);
|
|
301
|
-
const totalRow = createBlock(BASKET_SUMMARY_BLOCKS.TOTAL_ROW.id);
|
|
302
|
-
const checkoutSection = createBlock(
|
|
303
|
-
BASKET_SUMMARY_BLOCKS.CHECKOUT_SECTION.id
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
return [
|
|
307
|
-
createBlock(BASKET_SUMMARY_BLOCKS.TITLE.id),
|
|
308
|
-
{
|
|
309
|
-
...voucherSection,
|
|
310
|
-
blocks: [
|
|
311
|
-
createBlock(BASKET_SUMMARY_BLOCKS.VOUCHER_TITLE.id),
|
|
312
|
-
{
|
|
313
|
-
...voucherForm,
|
|
314
|
-
blocks: [
|
|
315
|
-
createBlock(BASKET_SUMMARY_BLOCKS.VOUCHER_INPUT.id),
|
|
316
|
-
createBlock(BASKET_SUMMARY_BLOCKS.VOUCHER_BUTTON.id)
|
|
317
|
-
]
|
|
318
|
-
}
|
|
319
|
-
]
|
|
320
|
-
},
|
|
321
|
-
{
|
|
322
|
-
...priceSection,
|
|
323
|
-
blocks: [
|
|
324
|
-
{
|
|
325
|
-
...subtotalRow,
|
|
326
|
-
blocks: [
|
|
327
|
-
createBlock(BASKET_SUMMARY_BLOCKS.SUBTOTAL_LABEL.id),
|
|
328
|
-
createBlock(BASKET_SUMMARY_BLOCKS.SUBTOTAL_PRICE.id)
|
|
329
|
-
]
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
...discountItemRow,
|
|
333
|
-
blocks: [
|
|
334
|
-
createBlock(BASKET_SUMMARY_BLOCKS.DISCOUNT_ITEM_LABEL.id),
|
|
335
|
-
createBlock(BASKET_SUMMARY_BLOCKS.DISCOUNT_ITEM_PRICE.id)
|
|
336
|
-
]
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
...discountsTotalRow,
|
|
340
|
-
blocks: [
|
|
341
|
-
createBlock(BASKET_SUMMARY_BLOCKS.DISCOUNTS_TOTAL_LABEL.id),
|
|
342
|
-
createBlock(BASKET_SUMMARY_BLOCKS.DISCOUNTS_TOTAL_PRICE.id)
|
|
343
|
-
]
|
|
344
|
-
},
|
|
345
|
-
{
|
|
346
|
-
...totalRow,
|
|
347
|
-
blocks: [
|
|
348
|
-
createBlock(BASKET_SUMMARY_BLOCKS.TOTAL_LABEL.id),
|
|
349
|
-
createBlock(BASKET_SUMMARY_BLOCKS.TOTAL_PRICE.id)
|
|
350
|
-
]
|
|
351
|
-
}
|
|
352
|
-
]
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
...checkoutSection,
|
|
356
|
-
blocks: [createBlock(BASKET_SUMMARY_BLOCKS.CHECKOUT_BUTTON.id)]
|
|
357
|
-
}
|
|
358
|
-
];
|
|
359
|
-
}, [createBlock]);
|
|
360
|
-
|
|
361
|
-
const indexBlocksRecursively = useCallback(
|
|
362
|
-
(blocks: ThemeBlock[], targetMap: Map<string, ThemeBlock>) => {
|
|
363
|
-
blocks.forEach((block) => {
|
|
364
|
-
targetMap.set(block.id, block);
|
|
365
|
-
|
|
366
|
-
if (block.blocks?.length) {
|
|
367
|
-
indexBlocksRecursively(block.blocks, targetMap);
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
},
|
|
371
|
-
[]
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
// Register native widget with Theme Editor (ONLY ONCE)
|
|
375
|
-
useEffect(() => {
|
|
376
|
-
const isInIframe =
|
|
377
|
-
typeof window !== 'undefined' && window.self !== window.top;
|
|
378
|
-
if (!isInIframe || !window.parent) {
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (hasRegisteredNativeWidget.current) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
const blocks = getDefaultBlocks();
|
|
387
|
-
|
|
388
|
-
window.parent.postMessage(
|
|
389
|
-
{
|
|
390
|
-
type: 'REGISTER_NATIVE_WIDGETS',
|
|
391
|
-
data: {
|
|
392
|
-
widgets: [
|
|
393
|
-
{
|
|
394
|
-
placeholderId: BASKET_SUMMARY_PLACEHOLDER_ID,
|
|
395
|
-
autoAdd: true,
|
|
396
|
-
section: {
|
|
397
|
-
id: BASKET_SUMMARY_SECTION_ID,
|
|
398
|
-
type: 'native',
|
|
399
|
-
label: 'Basket Summary',
|
|
400
|
-
blocks,
|
|
401
|
-
styles: BASKET_SUMMARY_SECTION_STYLES,
|
|
402
|
-
properties: {}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
]
|
|
406
|
-
}
|
|
407
|
-
},
|
|
408
|
-
'*'
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
hasRegisteredNativeWidget.current = true;
|
|
412
|
-
}, [getDefaultBlocks]);
|
|
413
|
-
|
|
414
|
-
// Load saved styles from widget (non-designer / standalone mode)
|
|
415
|
-
useEffect(() => {
|
|
416
|
-
if (designerState.isDesigner) return;
|
|
417
|
-
if (widgetData.isLoading) return;
|
|
418
|
-
|
|
419
|
-
if (
|
|
420
|
-
widgetData.sectionStyles &&
|
|
421
|
-
Object.keys(widgetData.sectionStyles).length > 0
|
|
422
|
-
) {
|
|
423
|
-
setSectionStyles(widgetData.sectionStyles);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
if (widgetData.blocks && widgetData.blocks.size > 0) {
|
|
427
|
-
const blockMap = new Map<string, ThemeBlock>();
|
|
428
|
-
widgetData.blocks.forEach((block) => {
|
|
429
|
-
blockMap.set(block.id, {
|
|
430
|
-
id: block.id,
|
|
431
|
-
type: block.type || 'text',
|
|
432
|
-
label: block.label || block.id,
|
|
433
|
-
styles: block.styles,
|
|
434
|
-
properties: block.properties
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
setThemeBlocks(blockMap);
|
|
439
|
-
themeBlocksRef.current = blockMap;
|
|
440
|
-
}
|
|
441
|
-
}, [
|
|
442
|
-
designerState.isDesigner,
|
|
443
|
-
widgetData.isLoading,
|
|
444
|
-
widgetData.sectionStyles,
|
|
445
|
-
widgetData.blocks
|
|
446
|
-
]);
|
|
447
|
-
|
|
448
|
-
// Listen for theme updates from Theme Editor
|
|
449
|
-
useEffect(() => {
|
|
450
|
-
const handleMessage = (event: MessageEvent) => {
|
|
451
|
-
const { type, data } = event.data || {};
|
|
452
|
-
|
|
453
|
-
if (type === 'SELECT_SECTION') {
|
|
454
|
-
const isOurSection =
|
|
455
|
-
data?.placeholderId === BASKET_SUMMARY_PLACEHOLDER_ID &&
|
|
456
|
-
data?.sectionId === BASKET_SUMMARY_SECTION_ID;
|
|
457
|
-
setIsSectionSelected(isOurSection);
|
|
458
|
-
if (isOurSection) {
|
|
459
|
-
setSelectedBlockId('');
|
|
460
|
-
}
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (type === 'SELECT_BLOCK') {
|
|
465
|
-
const isOurSectionBlock =
|
|
466
|
-
data?.placeholderId === BASKET_SUMMARY_PLACEHOLDER_ID &&
|
|
467
|
-
data?.sectionId === BASKET_SUMMARY_SECTION_ID;
|
|
468
|
-
if (isOurSectionBlock) {
|
|
469
|
-
setSelectedBlockId(data?.blockId || '');
|
|
470
|
-
setIsSectionSelected(false);
|
|
471
|
-
} else {
|
|
472
|
-
setSelectedBlockId('');
|
|
473
|
-
}
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
if (!designerState.isDesigner) {
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (
|
|
482
|
-
(type === 'UPDATE_THEME' || type === 'LOAD_THEME') &&
|
|
483
|
-
data?.theme?.placeholders
|
|
484
|
-
) {
|
|
485
|
-
const placeholder = data.theme.placeholders.find(
|
|
486
|
-
(p: ThemePlaceholder) => p.id === BASKET_SUMMARY_PLACEHOLDER_ID
|
|
487
|
-
);
|
|
488
|
-
|
|
489
|
-
if (!placeholder) return;
|
|
490
|
-
|
|
491
|
-
const summarySection = placeholder.sections?.find(
|
|
492
|
-
(section: ThemeSection) => section.id === BASKET_SUMMARY_SECTION_ID
|
|
493
|
-
);
|
|
494
|
-
|
|
495
|
-
if (!summarySection) return;
|
|
496
|
-
|
|
497
|
-
// Update section styles
|
|
498
|
-
if (summarySection.styles) {
|
|
499
|
-
setSectionStyles(summarySection.styles);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Update blocks map
|
|
503
|
-
const newBlocks = new Map<string, ThemeBlock>();
|
|
504
|
-
indexBlocksRecursively(summarySection.blocks || [], newBlocks);
|
|
505
|
-
|
|
506
|
-
// Merge: keep existing styles if incoming block has no styles
|
|
507
|
-
const currentBlocks = themeBlocksRef.current;
|
|
508
|
-
const mergedMap = new Map<string, ThemeBlock>();
|
|
509
|
-
|
|
510
|
-
currentBlocks.forEach((block, id) => mergedMap.set(id, block));
|
|
511
|
-
|
|
512
|
-
newBlocks.forEach((block, id) => {
|
|
513
|
-
const existingBlock = mergedMap.get(id);
|
|
514
|
-
const hasNewStyles =
|
|
515
|
-
block.styles && Object.keys(block.styles).length > 0;
|
|
516
|
-
|
|
517
|
-
if (hasNewStyles) {
|
|
518
|
-
mergedMap.set(id, block);
|
|
519
|
-
} else if (existingBlock) {
|
|
520
|
-
mergedMap.set(id, { ...block, styles: existingBlock.styles });
|
|
521
|
-
} else {
|
|
522
|
-
mergedMap.set(id, block);
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
themeBlocksRef.current = mergedMap;
|
|
527
|
-
setThemeBlocks(mergedMap);
|
|
528
|
-
hasReceivedThemeStyles.current = true;
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
window.addEventListener('message', handleMessage);
|
|
533
|
-
return () => window.removeEventListener('message', handleMessage);
|
|
534
|
-
}, [designerState.isDesigner, indexBlocksRecursively]);
|
|
535
|
-
|
|
536
|
-
const getSectionStyles = useCallback(() => {
|
|
537
|
-
return sectionStyles;
|
|
538
|
-
}, [sectionStyles]);
|
|
539
|
-
|
|
540
|
-
const getBlockData = useCallback(
|
|
541
|
-
(blockId: string) => {
|
|
542
|
-
return themeBlocks.get(blockId);
|
|
543
|
-
},
|
|
544
|
-
[themeBlocks]
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
const contextValue: BasketSummaryContextValue = {
|
|
548
|
-
isDesigner: designerState.isDesigner,
|
|
549
|
-
isSectionSelected,
|
|
550
|
-
selectedBlockId,
|
|
551
|
-
getSectionStyles,
|
|
552
|
-
getBlockData
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
return (
|
|
556
|
-
<BasketSummaryContext.Provider value={contextValue}>
|
|
557
|
-
{children}
|
|
558
|
-
</BasketSummaryContext.Provider>
|
|
559
|
-
);
|
|
560
|
-
};
|