@akinon/next 1.11.0 → 1.12.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/CHANGELOG.md +11 -0
- package/bin/pz-check-dependencies.js +98 -0
- package/bin/pz-install-plugins.js +1 -1
- package/bin/pz-install-theme.js +23 -6
- package/bin/pz-postinstall.js +3 -2
- package/bin/pz-prebuild.js +2 -3
- package/bin/pz-predev.js +2 -3
- package/bin/run-script.js +44 -0
- package/components/index.ts +1 -0
- package/components/lazy-component.tsx +33 -0
- package/components/plugin-module.tsx +8 -3
- package/components/react-portal.tsx +45 -0
- package/components/selected-payment-option-view.tsx +3 -0
- package/data/client/b2b.ts +16 -4
- package/data/client/wishlist.ts +16 -7
- package/package.json +3 -2
- package/types/commerce/b2b.ts +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 1.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ZERO-2314: Add endpoints for B2B Basket
|
|
8
|
+
- ZERO-2340: Add LazyComponent
|
|
9
|
+
- ZERO-2366: Add email parameter to addStockAlert query
|
|
10
|
+
- ZERO-2361: Add BKM Express to plugin module system
|
|
11
|
+
- ZERO-2365: Add dependency control script
|
|
12
|
+
- ZERO-2341: Add ReactPortal component
|
|
13
|
+
|
|
3
14
|
## 1.11.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const semver = require('semver');
|
|
6
|
+
|
|
7
|
+
function checkDir() {
|
|
8
|
+
let currentDir = __dirname;
|
|
9
|
+
|
|
10
|
+
while (currentDir !== path.resolve(currentDir, '..')) {
|
|
11
|
+
if (
|
|
12
|
+
fs.existsSync(
|
|
13
|
+
path.join(currentDir, 'node_modules/@akinon/next/package.json')
|
|
14
|
+
)
|
|
15
|
+
) {
|
|
16
|
+
return currentDir;
|
|
17
|
+
}
|
|
18
|
+
currentDir = path.resolve(currentDir, '..');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return path.resolve(__dirname, '../../../../');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const BASE_DIR = checkDir();
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const akinonNextPackagePath = fs.existsSync(
|
|
28
|
+
path.join(BASE_DIR, 'packages/akinon-next/package.json')
|
|
29
|
+
)
|
|
30
|
+
? path.join(BASE_DIR, 'packages/akinon-next/package.json')
|
|
31
|
+
: path.join(BASE_DIR, 'node_modules/@akinon/next/package.json');
|
|
32
|
+
|
|
33
|
+
let akinonNextPackage = JSON.parse(
|
|
34
|
+
fs.readFileSync(akinonNextPackagePath, 'utf8')
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const projectZeroPwaPackagePath = path.join(BASE_DIR, 'package.json');
|
|
38
|
+
|
|
39
|
+
const projectZeroPwaPackage = JSON.parse(
|
|
40
|
+
fs.readFileSync(projectZeroPwaPackagePath, 'utf8')
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const { peerDependencies } = akinonNextPackage;
|
|
44
|
+
|
|
45
|
+
let hasErrors = false;
|
|
46
|
+
|
|
47
|
+
let errorMessages = [];
|
|
48
|
+
|
|
49
|
+
for (const dependency in peerDependencies) {
|
|
50
|
+
const requiredVersion = peerDependencies[dependency];
|
|
51
|
+
|
|
52
|
+
const installedVersion =
|
|
53
|
+
projectZeroPwaPackage.dependencies[dependency] ||
|
|
54
|
+
projectZeroPwaPackage.devDependencies[dependency];
|
|
55
|
+
|
|
56
|
+
if (!installedVersion) {
|
|
57
|
+
errorMessages.push(
|
|
58
|
+
`Dependency ${dependency} is missing in projectzeropwa.`
|
|
59
|
+
);
|
|
60
|
+
hasErrors = true;
|
|
61
|
+
} else {
|
|
62
|
+
const requiredSemver = semver.coerce(requiredVersion);
|
|
63
|
+
|
|
64
|
+
const installedSemver = semver.coerce(installedVersion);
|
|
65
|
+
|
|
66
|
+
if (!requiredSemver || !installedSemver) {
|
|
67
|
+
errorMessages.push(
|
|
68
|
+
`Invalid semver for ${dependency}: required ${requiredVersion}, found ${installedVersion}`
|
|
69
|
+
);
|
|
70
|
+
hasErrors = true;
|
|
71
|
+
} else if (
|
|
72
|
+
requiredSemver.major !== installedSemver.major ||
|
|
73
|
+
requiredSemver.minor !== installedSemver.minor
|
|
74
|
+
) {
|
|
75
|
+
errorMessages.push(
|
|
76
|
+
`Version mismatch for ${dependency}: expected ${requiredVersion} or higher but found ${installedVersion}`
|
|
77
|
+
);
|
|
78
|
+
hasErrors = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (hasErrors) {
|
|
84
|
+
console.error(
|
|
85
|
+
'\x1b[31mDependency errors found:\n' +
|
|
86
|
+
errorMessages.join('\n') +
|
|
87
|
+
'\x1b[0m'
|
|
88
|
+
);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(
|
|
93
|
+
'All dependencies are installed and compatible in projectzeropwa.'
|
|
94
|
+
);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error('\x1b[31mError:', error.message + '\x1b[0m');
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
package/bin/pz-install-theme.js
CHANGED
|
@@ -2,15 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const spawn = require('cross-spawn');
|
|
6
|
+
|
|
7
|
+
function findBaseDir() {
|
|
8
|
+
const insideNodeModules = __dirname.includes('node_modules');
|
|
9
|
+
|
|
10
|
+
if (insideNodeModules) {
|
|
11
|
+
return path.resolve(__dirname, '../../../../');
|
|
12
|
+
} else {
|
|
13
|
+
return path.resolve(__dirname, '../../../apps/projectzeropwa');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const BASE_DIR = findBaseDir();
|
|
5
18
|
|
|
6
|
-
const BASE_DIR =
|
|
7
|
-
path.resolve(__dirname, '../../../apps/projectzeropwa') || process.cwd();
|
|
8
19
|
const getFullPath = (relativePath) => path.join(BASE_DIR, relativePath);
|
|
9
20
|
|
|
10
|
-
|
|
21
|
+
let theme;
|
|
22
|
+
try {
|
|
23
|
+
theme = require(getFullPath('src/theme.js'));
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error('Error loading theme.js:', error.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
11
28
|
|
|
12
29
|
try {
|
|
13
|
-
const spawn = require('cross-spawn');
|
|
14
30
|
const tsConfigPath = getFullPath('tsconfig.json');
|
|
15
31
|
|
|
16
32
|
if (!fs.existsSync(tsConfigPath)) {
|
|
@@ -31,11 +47,12 @@ try {
|
|
|
31
47
|
|
|
32
48
|
fs.writeFileSync(tsConfigPath, newContent);
|
|
33
49
|
|
|
34
|
-
if (fs.existsSync(getFullPath('
|
|
50
|
+
if (fs.existsSync(getFullPath('turbo.json'))) {
|
|
35
51
|
spawn.sync('turbo', ['clean'], {
|
|
36
52
|
stdio: 'inherit'
|
|
37
53
|
});
|
|
38
54
|
}
|
|
39
55
|
} catch (error) {
|
|
40
|
-
console.error('Error:', error.message);
|
|
56
|
+
console.error('Error modifying tsconfig.json:', error.message);
|
|
57
|
+
process.exit(1);
|
|
41
58
|
}
|
package/bin/pz-postinstall.js
CHANGED
package/bin/pz-prebuild.js
CHANGED
package/bin/pz-predev.js
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const spawn = require('cross-spawn');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
function runScript(scriptName) {
|
|
6
|
+
let currentPath;
|
|
7
|
+
|
|
8
|
+
const monorepoPath = path.join(
|
|
9
|
+
process.cwd(),
|
|
10
|
+
'../../packages/akinon-next/bin',
|
|
11
|
+
scriptName
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const standardRepoPath = path.join(
|
|
15
|
+
process.cwd(),
|
|
16
|
+
'node_modules/@akinon/next/bin',
|
|
17
|
+
scriptName
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (fs.existsSync(monorepoPath)) {
|
|
21
|
+
currentPath = monorepoPath;
|
|
22
|
+
} else if (fs.existsSync(standardRepoPath)) {
|
|
23
|
+
currentPath = standardRepoPath;
|
|
24
|
+
} else {
|
|
25
|
+
console.error(`Unable to find the ${scriptName} file`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const result = spawn.sync('node', [currentPath], { stdio: 'inherit' });
|
|
30
|
+
|
|
31
|
+
if (result.error) {
|
|
32
|
+
console.error(`Error executing ${scriptName}:`, result.error);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (result.status !== 0) {
|
|
37
|
+
console.error(
|
|
38
|
+
`Script ${scriptName} failed with exit code ${result.status}`
|
|
39
|
+
);
|
|
40
|
+
process.exit(result.status);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = runScript;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './react-portal';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import { useInView } from 'react-intersection-observer';
|
|
6
|
+
|
|
7
|
+
interface LazyComponentProps {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function LazyComponent({
|
|
13
|
+
children,
|
|
14
|
+
className
|
|
15
|
+
}: LazyComponentProps) {
|
|
16
|
+
const [isInView, setIsInView] = useState(false);
|
|
17
|
+
|
|
18
|
+
const { ref, inView } = useInView({
|
|
19
|
+
threshold: 0
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (inView) {
|
|
24
|
+
setIsInView(true);
|
|
25
|
+
}
|
|
26
|
+
}, [inView]);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div ref={ref} className={clsx(className)}>
|
|
30
|
+
{isInView ? <>{children}</> : null}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -10,7 +10,8 @@ enum Plugin {
|
|
|
10
10
|
PayOnDelivery = 'pz-pay-on-delivery',
|
|
11
11
|
CheckoutGiftPack = 'pz-checkout-gift-pack',
|
|
12
12
|
GPay = 'pz-gpay',
|
|
13
|
-
Otp = 'pz-otp'
|
|
13
|
+
Otp = 'pz-otp',
|
|
14
|
+
BKMExpress = 'pz-bkm'
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export enum Component {
|
|
@@ -20,7 +21,8 @@ export enum Component {
|
|
|
20
21
|
PayOnDelivery = 'PayOnDelivery',
|
|
21
22
|
CheckoutGiftPack = 'CheckoutGiftPack',
|
|
22
23
|
GPay = 'GPayOption',
|
|
23
|
-
Otp = 'Otp'
|
|
24
|
+
Otp = 'Otp',
|
|
25
|
+
BKMExpress = 'BKMOption'
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
const PluginComponents = new Map([
|
|
@@ -30,7 +32,8 @@ const PluginComponents = new Map([
|
|
|
30
32
|
[Plugin.PayOnDelivery, [Component.PayOnDelivery]],
|
|
31
33
|
[Plugin.CheckoutGiftPack, [Component.CheckoutGiftPack]],
|
|
32
34
|
[Plugin.GPay, [Component.GPay]],
|
|
33
|
-
[Plugin.Otp, [Component.Otp]]
|
|
35
|
+
[Plugin.Otp, [Component.Otp]],
|
|
36
|
+
[Plugin.BKMExpress, [Component.BKMExpress]]
|
|
34
37
|
]);
|
|
35
38
|
|
|
36
39
|
const getPlugin = (component: Component) => {
|
|
@@ -75,6 +78,8 @@ export default function PluginModule({
|
|
|
75
78
|
promise = import(`${'pz-gpay'}`);
|
|
76
79
|
} else if (plugin === Plugin.Otp) {
|
|
77
80
|
promise = import(`${'pz-otp'}`);
|
|
81
|
+
} else if (plugin === Plugin.BKMExpress) {
|
|
82
|
+
promise = import(`${'pz-bkm'}`);
|
|
78
83
|
}
|
|
79
84
|
} catch (error) {
|
|
80
85
|
logger.error(error);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
|
|
4
|
+
function createWrapperAndAppendToBody(wrapperId: string) {
|
|
5
|
+
const wrapperElement = document.createElement('div');
|
|
6
|
+
wrapperElement.setAttribute('id', wrapperId);
|
|
7
|
+
document.body.appendChild(wrapperElement);
|
|
8
|
+
return wrapperElement;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
wrapperId: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const ReactPortal: React.FC<Props> = ({
|
|
17
|
+
children,
|
|
18
|
+
wrapperId = 'react-portal-wrapper'
|
|
19
|
+
}) => {
|
|
20
|
+
const [wrapperElement, setWrapperElement] = useState<HTMLElement | null>(
|
|
21
|
+
null
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
let element = document.getElementById(wrapperId) as HTMLElement;
|
|
26
|
+
let modalCreated = false;
|
|
27
|
+
|
|
28
|
+
if (!element) {
|
|
29
|
+
modalCreated = true;
|
|
30
|
+
element = createWrapperAndAppendToBody(wrapperId);
|
|
31
|
+
}
|
|
32
|
+
setWrapperElement(element);
|
|
33
|
+
|
|
34
|
+
return () => {
|
|
35
|
+
// delete the programatically created element if it was created
|
|
36
|
+
if (modalCreated && element.parentNode) {
|
|
37
|
+
element.parentNode.removeChild(element);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}, [wrapperId]);
|
|
41
|
+
|
|
42
|
+
if (wrapperElement === null) return null;
|
|
43
|
+
|
|
44
|
+
return createPortal(children, wrapperElement);
|
|
45
|
+
};
|
|
@@ -54,6 +54,9 @@ export default function SelectedPaymentOptionView() {
|
|
|
54
54
|
// else if (payment_option.payment_type === 'gpay') {
|
|
55
55
|
// promise = import(`views/checkout/steps/payment/options/gpay`);
|
|
56
56
|
// }
|
|
57
|
+
// else if (payment_option.payment_type === 'bkm_express') {
|
|
58
|
+
// promise = import(`views/checkout/steps/payment/options/bkm`);
|
|
59
|
+
// }
|
|
57
60
|
} catch (error) {}
|
|
58
61
|
|
|
59
62
|
return promise
|
package/data/client/b2b.ts
CHANGED
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
GetResponse,
|
|
11
11
|
LoadBasketParams,
|
|
12
12
|
SaveBasketParams,
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
UpdateProductParams,
|
|
14
|
+
DeleteProductParams,
|
|
15
|
+
CreateQuotationParams
|
|
15
16
|
} from '../../types';
|
|
16
17
|
|
|
17
18
|
const b2bApi = api.injectEndpoints({
|
|
@@ -58,7 +59,7 @@ const b2bApi = api.injectEndpoints({
|
|
|
58
59
|
}),
|
|
59
60
|
invalidatesTags: ['BasketB2b']
|
|
60
61
|
}),
|
|
61
|
-
updateProduct: build.mutation<Basket,
|
|
62
|
+
updateProduct: build.mutation<Basket, UpdateProductParams>({
|
|
62
63
|
query: (body) => ({
|
|
63
64
|
url: buildClientRequestUrl(b2b.basket, {
|
|
64
65
|
contentType: 'application/json'
|
|
@@ -68,7 +69,17 @@ const b2bApi = api.injectEndpoints({
|
|
|
68
69
|
}),
|
|
69
70
|
invalidatesTags: ['BasketB2b']
|
|
70
71
|
}),
|
|
71
|
-
|
|
72
|
+
deleteProduct: build.mutation<Basket, DeleteProductParams>({
|
|
73
|
+
query: (body) => ({
|
|
74
|
+
url: buildClientRequestUrl(b2b.basket, {
|
|
75
|
+
contentType: 'application/json'
|
|
76
|
+
}),
|
|
77
|
+
method: 'DELETE',
|
|
78
|
+
body
|
|
79
|
+
}),
|
|
80
|
+
invalidatesTags: ['BasketB2b']
|
|
81
|
+
}),
|
|
82
|
+
createQuotation: build.mutation<BasketResponse, CreateQuotationParams>({
|
|
72
83
|
query: (body) => ({
|
|
73
84
|
url: buildClientRequestUrl(b2b.myQuotations, {
|
|
74
85
|
contentType: 'application/json'
|
|
@@ -90,5 +101,6 @@ export const {
|
|
|
90
101
|
useGetDraftsQuery,
|
|
91
102
|
useLoadBasketMutation,
|
|
92
103
|
useUpdateProductMutation,
|
|
104
|
+
useDeleteProductMutation,
|
|
93
105
|
useCreateQuotationMutation
|
|
94
106
|
} = b2bApi;
|
package/data/client/wishlist.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FavoriteItem } from '../../types';
|
|
2
2
|
import { buildClientRequestUrl } from '../../utils';
|
|
3
3
|
import { api } from './api';
|
|
4
|
-
import {
|
|
4
|
+
import { wishlist } from '../urls';
|
|
5
5
|
|
|
6
6
|
export type AddProductRequest = {
|
|
7
7
|
product: number;
|
|
@@ -28,6 +28,16 @@ interface RemoteFavoriteResponse {
|
|
|
28
28
|
success: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
interface AddStockAlertRequest {
|
|
32
|
+
productPk: number;
|
|
33
|
+
email?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface AddStockAlertResponse {
|
|
37
|
+
pk: number;
|
|
38
|
+
product: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
export const wishlistApi = api.injectEndpoints({
|
|
32
42
|
endpoints: (build) => ({
|
|
33
43
|
getFavorites: build.query<GetFavoritesResponse, GetFavoritesParams>({
|
|
@@ -54,16 +64,15 @@ export const wishlistApi = api.injectEndpoints({
|
|
|
54
64
|
}),
|
|
55
65
|
invalidatesTags: ['Favorite']
|
|
56
66
|
}),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
query: (productPk: number) => ({
|
|
67
|
+
addStockAlert: build.mutation<AddStockAlertResponse, AddStockAlertRequest>({
|
|
68
|
+
query: ({ productPk, email }) => ({
|
|
60
69
|
url: buildClientRequestUrl(wishlist.addStockAlert, {
|
|
61
|
-
useFormData: true
|
|
62
|
-
contentType: 'application/json'
|
|
70
|
+
useFormData: true
|
|
63
71
|
}),
|
|
64
72
|
method: 'POST',
|
|
65
73
|
body: {
|
|
66
|
-
product: productPk
|
|
74
|
+
product: productPk,
|
|
75
|
+
...(email ? { email } : {})
|
|
67
76
|
}
|
|
68
77
|
})
|
|
69
78
|
})
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/next",
|
|
3
3
|
"description": "Core package for Project Zero Next",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.12.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"cross-spawn": "7.0.3",
|
|
19
19
|
"react-redux": "8.1.3",
|
|
20
20
|
"react-string-replace": "1.1.1",
|
|
21
|
-
"redis": "4.5.1"
|
|
21
|
+
"redis": "4.5.1",
|
|
22
|
+
"semver": "7.5.4"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/react-redux": "7.1.30"
|
package/types/commerce/b2b.ts
CHANGED
|
@@ -22,7 +22,7 @@ export type Division = {
|
|
|
22
22
|
extra_data: object;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
interface ProductB2b extends Product {
|
|
25
|
+
export interface ProductB2b extends Product {
|
|
26
26
|
sku: string;
|
|
27
27
|
name: string;
|
|
28
28
|
price: string;
|
|
@@ -84,16 +84,20 @@ export type SaveBasketParams = {
|
|
|
84
84
|
name: string;
|
|
85
85
|
};
|
|
86
86
|
|
|
87
|
-
export type
|
|
87
|
+
export type CreateQuotationParams = {
|
|
88
88
|
name: string;
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
export type
|
|
91
|
+
export type UpdateProductParams = {
|
|
92
92
|
product_remote_id: number;
|
|
93
93
|
division: number;
|
|
94
94
|
quantity: number
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
+
export type DeleteProductParams = {
|
|
98
|
+
product_remote_id: number;
|
|
99
|
+
};
|
|
100
|
+
|
|
97
101
|
export type LoadBasketParams = {
|
|
98
102
|
id: number;
|
|
99
103
|
};
|