@akinon/pz-b2b 1.18.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +7 -0
- package/README.md +234 -0
- package/package.json +18 -0
- package/src/basket-b2b/index.tsx +220 -0
- package/src/hook/use-b2b.tsx +50 -0
- package/src/index.ts +6 -0
- package/src/my-quotations/index.tsx +136 -0
- package/src/utils/gtm.ts +26 -0
- package/src/views/basket-b2b/basket-item.tsx +144 -0
- package/src/views/basket-b2b/index.ts +4 -0
- package/src/views/basket-b2b/remove-product-modal.tsx +109 -0
- package/src/views/basket-b2b/request-quote-modal.tsx +97 -0
- package/src/views/basket-b2b/save-cart-modal.tsx +80 -0
- package/src/views/my-quotations/tabs.tsx +184 -0
- package/src/views/product/store-modal.tsx +170 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { useAddToBasketMutation } from '@akinon/next/data/client/b2b';
|
4
|
+
import { useLocalization } from '@akinon/next/hooks';
|
5
|
+
import { Division } from '@akinon/next/types';
|
6
|
+
import React, { useMemo, useState } from 'react';
|
7
|
+
import { twMerge } from 'tailwind-merge';
|
8
|
+
|
9
|
+
interface GetResponse<T> {
|
10
|
+
count: number;
|
11
|
+
next: null;
|
12
|
+
previous: null;
|
13
|
+
results: T[];
|
14
|
+
}
|
15
|
+
|
16
|
+
export function StoreModal({
|
17
|
+
storeData,
|
18
|
+
productPk,
|
19
|
+
open,
|
20
|
+
setOpen
|
21
|
+
}: {
|
22
|
+
storeData: GetResponse<Division>;
|
23
|
+
productPk: number;
|
24
|
+
open: boolean;
|
25
|
+
setOpen: (open: boolean) => void;
|
26
|
+
}) {
|
27
|
+
const initialStateSelectStore = {
|
28
|
+
id: null,
|
29
|
+
quantity: '0'
|
30
|
+
};
|
31
|
+
|
32
|
+
const { t } = useLocalization();
|
33
|
+
const [addToBasket] = useAddToBasketMutation();
|
34
|
+
const [selectedStore, setSelectedStore] = useState(initialStateSelectStore);
|
35
|
+
const inputRefs = {};
|
36
|
+
|
37
|
+
const handleRadioChange = (storeId) => {
|
38
|
+
setSelectedStore((prevSelectedStore) => ({
|
39
|
+
...prevSelectedStore,
|
40
|
+
id: storeId
|
41
|
+
}));
|
42
|
+
|
43
|
+
clearQuantityInput(storeId);
|
44
|
+
};
|
45
|
+
|
46
|
+
const handleQuantityChange = (storeId, e) => {
|
47
|
+
if (storeId !== selectedStore.id) {
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
|
51
|
+
setSelectedStore((prevSelectedStore) => ({
|
52
|
+
...prevSelectedStore,
|
53
|
+
quantity: e.target.value
|
54
|
+
}));
|
55
|
+
};
|
56
|
+
|
57
|
+
const handleAddToBasket = () => {
|
58
|
+
addToBasket({
|
59
|
+
division: selectedStore.id,
|
60
|
+
product_remote_id: productPk.toString(),
|
61
|
+
quantity: selectedStore.quantity
|
62
|
+
})
|
63
|
+
.unwrap()
|
64
|
+
.then(() => {
|
65
|
+
setOpen(false);
|
66
|
+
setSelectedStore(initialStateSelectStore);
|
67
|
+
});
|
68
|
+
};
|
69
|
+
|
70
|
+
const clearQuantityInput = (storeId) => {
|
71
|
+
inputRefs[storeId]?.focus();
|
72
|
+
inputRefs[storeId].value = '';
|
73
|
+
|
74
|
+
setSelectedStore((prevSelectedStore) => ({
|
75
|
+
...prevSelectedStore,
|
76
|
+
quantity: '0'
|
77
|
+
}));
|
78
|
+
};
|
79
|
+
|
80
|
+
const isButtonDisabled = useMemo(() => {
|
81
|
+
if (selectedStore.quantity === '') {
|
82
|
+
return true;
|
83
|
+
}
|
84
|
+
|
85
|
+
return !selectedStore.id || parseInt(selectedStore.quantity) < 1;
|
86
|
+
}, [selectedStore]);
|
87
|
+
|
88
|
+
if (!open) return null;
|
89
|
+
|
90
|
+
return (
|
91
|
+
<div className="before:w-full before:h-screen before:content-[''] before:bg-black/60 before:z-40 before:fixed before:inset-0">
|
92
|
+
<div className="w-full h-screen inset-0 md:inset-auto md:w-[600px] md:h-min md:left-1/2 md:top-1/2 md:-translate-x-1/2 md:-translate-y-1/2 z-50 fixed bg-white">
|
93
|
+
<header className="relative md:border-b border-[#d5dadb]">
|
94
|
+
<h3 className="text-left px-4 md:text-center py-4 font-bold text-lg">
|
95
|
+
{t('product.store_select_modal.title')}
|
96
|
+
</h3>
|
97
|
+
<button
|
98
|
+
className="absolute right-[15px] md:right-[30px] top-5"
|
99
|
+
onClick={() => {
|
100
|
+
setOpen(false);
|
101
|
+
setSelectedStore(initialStateSelectStore);
|
102
|
+
}}
|
103
|
+
>
|
104
|
+
<i className="pz-icon-close"></i>
|
105
|
+
</button>
|
106
|
+
</header>
|
107
|
+
<div className="p-[15px] md:p-[30px]">
|
108
|
+
<table className="w-full flex flex-col h-full md:h-auto md:table md:w-[540px]">
|
109
|
+
<thead className="w-full flex-grow-0 shrink-0 basis-auto table">
|
110
|
+
<tr className="border border-[#d7d7d7] bg-[#fafafa]">
|
111
|
+
<th className="w-[43px] border-r border-[#d7d7d7]"></th>
|
112
|
+
<th className="w-[190px] md:w-auto text-[11px] text-[#99999d] py-[18px] px-4 border-r border-[#d7d7d7] text-left font-normal">
|
113
|
+
{t('product.store_select_modal.store_name')}
|
114
|
+
</th>
|
115
|
+
<th className="max-w-[111px] w-[111px] md:w-[180px] md:max-w-[180px] text-[11px] text-[#99999d] py-[18px] text-right pr-6 md:pr-[37px] font-normal">
|
116
|
+
{t('product.store_select_modal.quantity')}
|
117
|
+
</th>
|
118
|
+
</tr>
|
119
|
+
</thead>
|
120
|
+
<tbody className="flex-1 basis-auto block overflow-y-auto h-[calc(100vh-206px)] md:h-auto md:max-h-[488px]">
|
121
|
+
{storeData?.results.map((store) => (
|
122
|
+
<tr
|
123
|
+
key={store.id}
|
124
|
+
className="w-full table table-fixed h-[61px] even:bg-[#fafafa] border border-t-0 border-[#d7d7d7]"
|
125
|
+
>
|
126
|
+
<td className="w-[43px] border-r border-[#d7d7d7] text-center">
|
127
|
+
<input
|
128
|
+
type="radio"
|
129
|
+
value={store.id}
|
130
|
+
name="division"
|
131
|
+
className="w-[18px] h-[18px] accent-black"
|
132
|
+
id={store.erp_code}
|
133
|
+
onChange={() => handleRadioChange(store.id)}
|
134
|
+
/>
|
135
|
+
</td>
|
136
|
+
<td className="border-r border-[#d7d7d7] text-xs px-4 py-6 leading-3 w-[190px] md:w-auto">
|
137
|
+
<label htmlFor={store.erp_code}>{store.name}</label>
|
138
|
+
</td>
|
139
|
+
<td className="text-right pr-6 md:pr-[37px] max-w-[111px] w-[111px] md:w-[180px] md:max-w-[180px]">
|
140
|
+
<input
|
141
|
+
type="number"
|
142
|
+
className="w-[64px] md:w-[84px] h-[31px] border border-[#d7d7d7] outline-none text-xs text-right pr-1 appearance-none"
|
143
|
+
onChange={(e) => handleQuantityChange(store.id, e)}
|
144
|
+
ref={(ref) => (inputRefs[store.id] = ref)}
|
145
|
+
/>
|
146
|
+
</td>
|
147
|
+
</tr>
|
148
|
+
))}
|
149
|
+
</tbody>
|
150
|
+
</table>
|
151
|
+
|
152
|
+
<div className="fixed bottom-0 left-0 w-full p-[15px] md:p-0 md:static mt-5 shadow-[0_0_4px_0_rgba(0,0,0,0.2)] md:shadow-none">
|
153
|
+
<p hidden>{t('product.store_select_modal.valid_quantity')}</p>
|
154
|
+
|
155
|
+
<button
|
156
|
+
className={twMerge(
|
157
|
+
'text-xs text-white bg-black py-3 px-14 w-full md:w-auto',
|
158
|
+
isButtonDisabled && 'bg-black/40'
|
159
|
+
)}
|
160
|
+
onClick={handleAddToBasket}
|
161
|
+
disabled={isButtonDisabled}
|
162
|
+
>
|
163
|
+
{t('product.store_select_modal.add_to_basket')}
|
164
|
+
</button>
|
165
|
+
</div>
|
166
|
+
</div>
|
167
|
+
</div>
|
168
|
+
</div>
|
169
|
+
);
|
170
|
+
}
|