@akinon/pz-b2b 1.19.4 → 1.21.0-rc.0

Sign up to get free protection for your applications and to get access to all the features.
package/.prettierrc ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "bracketSameLine": false,
3
+ "tabWidth": 2,
4
+ "singleQuote": true,
5
+ "jsxSingleQuote": false,
6
+ "bracketSpacing": true,
7
+ "semi": true,
8
+ "useTabs": false,
9
+ "arrowParens": "always",
10
+ "endOfLine": "lf",
11
+ "proseWrap": "never",
12
+ "trailingComma": "none"
13
+ }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @akinon/pz-b2b
2
2
 
3
+ ## 1.21.0-rc.0
4
+
5
+ ## 1.20.0
6
+
7
+ ### Minor Changes
8
+
9
+ - 86a9561: ZERO-2416: Update translations and refactor my quotations
10
+
3
11
  ## 1.19.4
4
12
 
5
13
  ## 1.19.3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/pz-b2b",
3
- "version": "1.19.4",
3
+ "version": "1.21.0-rc.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.ts",
6
6
  "peerDependencies": {
@@ -1,31 +1,72 @@
1
- "use client";
1
+ 'use client';
2
2
 
3
- import { useEffect, useState } from "react";
4
- import { pushCartView } from "../utils/gtm";
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import { pushCartView } from '../utils/gtm';
5
5
  import {
6
6
  LoaderSpinner,
7
7
  Trans,
8
8
  Price,
9
9
  Icon,
10
- Link,
11
- } from "@akinon/next/components";
12
- import { useLocalization } from "@akinon/next/hooks";
10
+ Link
11
+ } from '@akinon/next/components';
13
12
  import {
14
13
  useGetBasketB2bQuery,
15
14
  useGetDraftsQuery,
16
- useLoadBasketMutation,
17
- } from "@akinon/next/data/client/b2b";
15
+ useLoadBasketMutation
16
+ } from '@akinon/next/data/client/b2b';
18
17
  import {
19
18
  BasketItem,
20
19
  RequestQuoteModal,
21
20
  RemoveProductModal,
22
- SaveCartModal,
23
- } from "../views/basket-b2b";
21
+ SaveCartModal
22
+ } from '../views/basket-b2b';
24
23
 
25
- export function BasketB2b() {
24
+ interface BasketB2bTranslationsProps {
25
+ my_cart: string;
26
+ back_to_shopping: string;
27
+ save_cart: string;
28
+ request_quote: string;
29
+ total_price: string;
30
+ save_cart_modal: {
31
+ title: string;
32
+ cart_name: string;
33
+ save_cart_button: string;
34
+ };
35
+ request_quote_modal: {
36
+ title: string;
37
+ quotation_name: string;
38
+ button: string;
39
+ };
40
+ remove_product_modal: {
41
+ title: string;
42
+ description: string;
43
+ add_to_favorites_button: string;
44
+ delete_product_button: string;
45
+ };
46
+ empty: {
47
+ title: string;
48
+ button: string;
49
+ choose_cart_placeholder: string;
50
+ };
51
+ table: {
52
+ basket_qty: string;
53
+ delete: string;
54
+ name_sku: string;
55
+ price: string;
56
+ quantity: string;
57
+ store_name: string;
58
+ store_qty: string;
59
+ subtotal: string;
60
+ };
61
+ }
62
+
63
+ interface BasketB2bProps {
64
+ translations: Record<string, BasketB2bTranslationsProps>;
65
+ }
66
+
67
+ export function BasketB2b({ translations }: BasketB2bProps) {
26
68
  const { data: basket, isLoading, isSuccess } = useGetBasketB2bQuery();
27
69
  const [loadBasket] = useLoadBasketMutation();
28
- const { t } = useLocalization();
29
70
  const { data: dataDraft } = useGetDraftsQuery();
30
71
  const [open, setOpen] = useState(false);
31
72
  const [openRequestQuote, setRequestQuote] = useState(false);
@@ -33,6 +74,52 @@ export function BasketB2b() {
33
74
  const [productToBeDeleted, setProductToBeDeleted] = useState(null);
34
75
  const [selectedDraft, setSelectedDraft] = useState(null);
35
76
 
77
+ const defaultTranslations = {
78
+ my_cart: 'My Cart',
79
+ back_to_shopping: 'BACK TO SHOPPING',
80
+ save_cart: 'SAVE CART',
81
+ request_quote: 'REQUEST A QUOTE',
82
+ total_price: 'Catalog Total Price (Excl. Tax)',
83
+ save_cart_modal: {
84
+ title: 'SAVE CART',
85
+ cart_name: 'Cart Name',
86
+ save_cart_button: 'SAVE CART'
87
+ },
88
+ request_quote_modal: {
89
+ title: 'REQUEST A QUOTE',
90
+ quotation_name: 'Quotation Name',
91
+ button: 'SAVE'
92
+ },
93
+ remove_product_modal: {
94
+ title: 'DELETE AND ADD TO FAVORITES',
95
+ description:
96
+ 'Would you like to add this product you want to delete to your favorites?',
97
+ add_to_favorites_button: 'ADD TO FAVORITES',
98
+ delete_product_button: 'delete product'
99
+ },
100
+ empty: {
101
+ title:
102
+ 'Your cart is empty, would you like to continue with your saved carts?',
103
+ button: 'SELECT CART',
104
+ choose_cart_placeholder: 'Chose a cart'
105
+ },
106
+ table: {
107
+ basket_qty: '<Qty /> Products',
108
+ delete: 'Delete',
109
+ name_sku: 'NAME & SKU',
110
+ price: 'PRICE',
111
+ quantity: 'QTY',
112
+ store_name: 'STORE NAME',
113
+ store_qty: 'STORE QTY',
114
+ subtotal: 'SUBTOTAL'
115
+ }
116
+ };
117
+
118
+ const _translations = useMemo(
119
+ () => ({ ...defaultTranslations, ...translations }),
120
+ [translations]
121
+ );
122
+
36
123
  const handleSubmit = (e) => {
37
124
  e.preventDefault();
38
125
 
@@ -46,7 +133,7 @@ export function BasketB2b() {
46
133
  useEffect(() => {
47
134
  if (isSuccess) {
48
135
  const products = basket?.basket_items.map((basketItem) => ({
49
- ...basketItem.product,
136
+ ...basketItem.product
50
137
  }));
51
138
  pushCartView(products);
52
139
  }
@@ -67,19 +154,19 @@ export function BasketB2b() {
67
154
  <div className="w-full">
68
155
  <div className="flex items-center justify-between py-2 lg:py-3">
69
156
  <h2 className="text-xl lg:text-2xl font-light">
70
- {t("basket.b2b.my_cart")}
157
+ {_translations.my_cart}
71
158
  </h2>
72
159
 
73
160
  <Link href="/" className="text-xs hover:text-secondary-500">
74
- {t("basket.b2b.back_to_shopping")}
161
+ {_translations.back_to_shopping}
75
162
  </Link>
76
163
  </div>
77
164
 
78
165
  <div className="px-5 py-4 border border-b-0 border-gray-200 text-xs text-[#363636]">
79
166
  <Trans
80
- i18nKey="basket.b2b.table.basket_qty"
167
+ i18nKey={_translations.table.basket_qty}
81
168
  components={{
82
- Qty: <span>{basket.total_quantity}</span>,
169
+ Qty: <span>{basket.total_quantity}</span>
83
170
  }}
84
171
  />
85
172
  </div>
@@ -99,20 +186,20 @@ export function BasketB2b() {
99
186
  </div>
100
187
  </th>
101
188
  <th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
102
- {t("basket.b2b.table.name_sku")}
189
+ {_translations.table.name_sku}
103
190
  </th>
104
191
  <th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
105
- {t("basket.b2b.table.price")}
192
+ {_translations.table.price}
106
193
  </th>
107
194
  <th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
108
- {t("basket.b2b.table.quantity")}
195
+ {_translations.table.quantity}
109
196
  </th>
110
197
  <th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
111
- {t("basket.b2b.table.store_name")}
198
+ {_translations.table.store_name}
112
199
  </th>
113
200
  <th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
114
201
  <div className="flex items-center justify-between pl-5 pr-2.5">
115
- <span>{t("basket.b2b.table.store_qty")}</span>
202
+ <span>{_translations.table.store_qty}</span>
116
203
  <Icon
117
204
  name="b2b-distribute"
118
205
  size={18}
@@ -121,7 +208,7 @@ export function BasketB2b() {
121
208
  </div>
122
209
  </th>
123
210
  <th className="text-xs text-[#99999d] font-normal border border-[#d5dadb] py-3">
124
- {t("basket.b2b.table.subtotal")}
211
+ {_translations.table.subtotal}
125
212
  </th>
126
213
  </tr>
127
214
  </thead>
@@ -133,6 +220,7 @@ export function BasketB2b() {
133
220
  key={index}
134
221
  setRemoveProduct={setRemoveProduct}
135
222
  setProductToBeDeleted={setProductToBeDeleted}
223
+ deleteText={_translations.table.delete}
136
224
  />
137
225
  ))}
138
226
  </tbody>
@@ -143,7 +231,7 @@ export function BasketB2b() {
143
231
  <div className="flex justify-end">
144
232
  <div className="w-full md:w-[426px]">
145
233
  <div className="w-full flex justify-between items-center text-sm text-[#3f4b5c] border border-[#d5dadb] py-6 px-4 mt-7 md:mt-0 md:px-5">
146
- <span>{t("basket.b2b.total_price")}</span>
234
+ <span>{_translations.total_price}</span>
147
235
  <Price value={basket.total_amount} />
148
236
  </div>
149
237
 
@@ -151,14 +239,14 @@ export function BasketB2b() {
151
239
  onClick={() => setRequestQuote(true)}
152
240
  className="mt-4 lg:mt-5 w-full h-10 text-white bg-black border border-black text-sm hover:bg-white hover:text-black transition-colors"
153
241
  >
154
- {t("basket.b2b.request_quote")}
242
+ {_translations.request_quote}
155
243
  </button>
156
244
 
157
245
  <button
158
246
  onClick={() => setOpen(true)}
159
247
  className="mt-4 lg:mt-2.5 w-full h-10 bg-white text-[#2189ff] border border-[#2189ff] text-sm hover:bg-[#2189ff] hover:text-white transition-colors"
160
248
  >
161
- {t("basket.b2b.save_cart")}
249
+ {_translations.empty.title}
162
250
  </button>
163
251
  </div>
164
252
  </div>
@@ -175,7 +263,7 @@ export function BasketB2b() {
175
263
  0
176
264
  </span>
177
265
  </div>
178
- <p className="text-xs">{t("basket.b2b.empty.title")}</p>
266
+ <p className="text-xs">{_translations.empty.title}</p>
179
267
  </div>
180
268
 
181
269
  <form onSubmit={(e) => handleSubmit(e)}>
@@ -186,7 +274,7 @@ export function BasketB2b() {
186
274
  className="w-[230px] h-[40px] mb-1 border border-[#d4d4d4] px-4 appearance-none text-sm outline-none cursor-pointer"
187
275
  >
188
276
  <option value="">
189
- {t("basket.b2b.empty.choose_cart_placeholder")}
277
+ {_translations.empty.choose_cart_placeholder}
190
278
  </option>
191
279
  {dataDraft &&
192
280
  dataDraft.map((draft) => (
@@ -201,18 +289,38 @@ export function BasketB2b() {
201
289
  type="submit"
202
290
  className="px-10 mt-2 w-full md:w-[230px] h-[40px] text-sm font-medium bg-black text-white cursor-pointer"
203
291
  >
204
- {t("basket.empty.button")}
292
+ {_translations.empty.button}
205
293
  </button>
206
294
  </form>
207
295
  </div>
208
296
  ))}
209
297
 
210
- <SaveCartModal setOpen={setOpen} open={open} />
211
- <RequestQuoteModal setOpen={setRequestQuote} open={openRequestQuote} />
298
+ <SaveCartModal
299
+ setOpen={setOpen}
300
+ open={open}
301
+ modalTitle={_translations.save_cart_modal.title}
302
+ modalCartName={_translations.save_cart_modal.cart_name}
303
+ saveCartButton={_translations.save_cart_modal.save_cart_button}
304
+ />
305
+ <RequestQuoteModal
306
+ setOpen={setRequestQuote}
307
+ open={openRequestQuote}
308
+ modalTitle={_translations.request_quote_modal.title}
309
+ modalQuotationName={_translations.request_quote_modal.quotation_name}
310
+ modalButton={_translations.request_quote_modal.button}
311
+ />
212
312
  <RemoveProductModal
213
313
  setOpen={setRemoveProduct}
214
314
  open={openRemoveProduct}
215
315
  basketItem={productToBeDeleted}
316
+ modalTitle={_translations.remove_product_modal.title}
317
+ modalDescription={_translations.remove_product_modal.description}
318
+ addToFavoritesButton={
319
+ _translations.remove_product_modal.add_to_favorites_button
320
+ }
321
+ deleteProductButton={
322
+ _translations.remove_product_modal.delete_product_button
323
+ }
216
324
  />
217
325
  </div>
218
326
  </>
@@ -1,45 +1,126 @@
1
1
  'use client';
2
2
 
3
- import { useGetQuotationsQuery } from '@akinon/next/data/client/account';
4
- import { Quotations } from '@akinon/next/types';
3
+ import {
4
+ GetQuotationsResponse,
5
+ useGetQuotationsQuery
6
+ } from '@akinon/next/data/client/account';
5
7
  import { LoaderSpinner, Link, Pagination } from '@akinon/next/components';
6
8
  import { Tabs } from '../views/my-quotations/tabs';
7
9
  import clsx from 'clsx';
8
- import React, { useEffect, useState } from 'react';
10
+ import React, { useEffect, useMemo, useState } from 'react';
9
11
  import { twMerge } from 'tailwind-merge';
10
- import { useSearchParams } from 'next/navigation';
12
+ import { useSearchParams, usePathname } from 'next/navigation';
13
+ import { useRouter } from '@akinon/next/hooks';
11
14
 
12
- export function AccountMyQuotations() {
13
- const searchParams = useSearchParams();
14
- const { data: quotations, isLoading } = useGetQuotationsQuery();
15
+ interface AccountMyQuotationsTranslationsProps {
16
+ tableName: string;
17
+ tableDate: string;
18
+ tableStatus: string;
19
+ tableNumber: string;
20
+ tableActions: string;
21
+ tabAllQuotas: string;
22
+ tabPendingQuotas: string;
23
+ tabRejectedQuotas: string;
24
+ tabApprovedQuotas: string;
25
+ tabRequiresMyApproval: string;
26
+ tabTitleAllQuotas: string;
27
+ tabTitlePendingQuotas: string;
28
+ tabTitleRejectedQuotas: string;
29
+ tabTitleApprovedQuotas: string;
30
+ tabTitleRequiresMyApproval: string;
31
+ }
32
+ interface AccountMyQuotationsProps {
33
+ translations: Record<string, AccountMyQuotationsTranslationsProps>;
34
+ }
35
+
36
+ export function AccountMyQuotations({
37
+ translations
38
+ }: AccountMyQuotationsProps) {
39
+ const searchParams = new URLSearchParams(useSearchParams());
40
+ const router = useRouter();
41
+ const pathname = usePathname();
42
+ const { data: quotations, isLoading } = useGetQuotationsQuery({
43
+ page: Number(searchParams.get('page') || 1)
44
+ });
45
+ const { data: quotationsPending } = useGetQuotationsQuery({
46
+ page: Number(searchParams.get('page') || 1),
47
+ status: 'pending'
48
+ });
49
+ const { data: quotationsRejected } = useGetQuotationsQuery({
50
+ page: Number(searchParams.get('page') || 1),
51
+ status: 'rejected'
52
+ });
53
+ const { data: quotationsApproved } = useGetQuotationsQuery({
54
+ page: Number(searchParams.get('page') || 1),
55
+ status: 'approved'
56
+ });
57
+ const { data: quotationsSendingApprovalRequest } = useGetQuotationsQuery({
58
+ page: Number(searchParams.get('page') || 1),
59
+ status: 'sending_approval_request'
60
+ });
15
61
  const [activeTab, setActiveTab] = useState('all');
16
- const [filteredQuotations, setFilteredQuotations] = useState<Quotations[]>(
17
- []
62
+ const [selectedQuotations, setSelectedQuotations] =
63
+ useState<GetQuotationsResponse>();
64
+
65
+ const defaultTranslations = {
66
+ tableName: 'Name',
67
+ tableDate: 'Date',
68
+ tableStatus: 'Status',
69
+ tableNumber: 'Number',
70
+ tableActions: 'Actions',
71
+ tabAllQuotas: 'All Quotas',
72
+ tabPendingQuotas: 'Pending',
73
+ tabRejectedQuotas: 'Rejected',
74
+ tabApprovedQuotas: 'Approved',
75
+ tabRequiresMyApproval: 'Requires My Approval',
76
+ tabTitleAllQuotas: 'All Quotas',
77
+ tabTitlePendingQuotas: 'Pending Quotas',
78
+ tabTitleRejectedQuotas: 'Rejected Quotas',
79
+ tabTitleApprovedQuotas: 'Approved Quotas',
80
+ tabTitleRequiresMyApproval: 'Requires My Approval'
81
+ };
82
+
83
+ const _translations = useMemo(
84
+ () => ({ ...defaultTranslations, ...translations }),
85
+ [translations]
18
86
  );
19
87
 
20
88
  const thClasses =
21
89
  'px-6 py-3 bg-[#f1f3f9] text-xs text-[#99999d] border-r border-[#c8daec] font-normal text-left';
22
- const thTitles = ['NAME', 'DATE', 'STATUS', 'NUMBER', 'ACTIONS'];
23
-
24
- const filterStatus = (quotation: Quotations, activeStatus: string) => {
25
- if (activeStatus === 'all') {
26
- return quotation;
27
- }
28
-
29
- return quotation.status === activeStatus;
30
- };
90
+ const thTitles = [
91
+ _translations.tableName,
92
+ _translations.tableDate,
93
+ _translations.tableStatus,
94
+ _translations.tableNumber,
95
+ _translations.tableActions
96
+ ];
31
97
 
32
98
  useEffect(() => {
33
- if (!quotations) {
34
- return;
99
+ if (activeTab === 'all') {
100
+ setSelectedQuotations(quotations);
101
+ } else if (activeTab === 'pending') {
102
+ setSelectedQuotations(quotationsPending);
103
+ } else if (activeTab === 'rejected') {
104
+ setSelectedQuotations(quotationsRejected);
105
+ } else if (activeTab === 'approved') {
106
+ setSelectedQuotations(quotationsApproved);
107
+ } else if (activeTab === 'sending_approval_request') {
108
+ setSelectedQuotations(quotationsSendingApprovalRequest);
35
109
  }
110
+ }, [
111
+ selectedQuotations,
112
+ activeTab,
113
+ quotations,
114
+ quotationsPending,
115
+ quotationsRejected,
116
+ quotationsApproved,
117
+ quotationsSendingApprovalRequest
118
+ ]);
36
119
 
37
- const _filteredQuotations = quotations.results.filter((quotation) =>
38
- filterStatus(quotation, activeTab)
39
- );
40
-
41
- setFilteredQuotations(_filteredQuotations);
42
- }, [quotations, activeTab]);
120
+ useEffect(() => {
121
+ searchParams.delete('page');
122
+ router.push(pathname + '?' + searchParams.toString());
123
+ }, [activeTab]);
43
124
 
44
125
  if (isLoading) {
45
126
  return <LoaderSpinner />;
@@ -50,19 +131,36 @@ export function AccountMyQuotations() {
50
131
  <Tabs
51
132
  setActiveTab={setActiveTab}
52
133
  activeTab={activeTab}
53
- quotations={quotations}
134
+ quotationsCount={quotations?.count}
135
+ pendingCount={quotationsPending?.count}
136
+ rejectedCount={quotationsRejected?.count}
137
+ approvedCount={quotationsApproved?.count}
138
+ sendingApprovalRequestCount={quotationsSendingApprovalRequest?.count}
139
+ tabAllQuotas={_translations.tabAllQuotas}
140
+ tabPendingQuotas={_translations.tabPendingQuotas}
141
+ tabRejectedQuotas={_translations.tabRejectedQuotas}
142
+ tabApprovedQuotas={_translations.tabApprovedQuotas}
143
+ tabRequiresMyApproval={_translations.tabRequiresMyApproval}
54
144
  />
55
145
 
56
146
  <div className="border border-[#d5dadb] mb-10 overflow-x-auto lg:overflow-hidden">
57
147
  <p className="px-6 py-5 text-[#363636]">
58
- <span hidden={activeTab !== 'all'}>All Quotas</span>
59
- <span hidden={activeTab !== 'pending'}>Pending Quotas</span>
60
- <span hidden={activeTab !== 'rejected'}>Rejected Quotas</span>
61
- <span hidden={activeTab !== 'approved'}>Approved Quotas</span>
148
+ <span hidden={activeTab !== 'all'}>
149
+ {_translations.tabTitleAllQuotas}
150
+ </span>
151
+ <span hidden={activeTab !== 'pending'}>
152
+ {_translations.tabTitlePendingQuotas}
153
+ </span>
154
+ <span hidden={activeTab !== 'rejected'}>
155
+ {_translations.tabTitleRejectedQuotas}
156
+ </span>
157
+ <span hidden={activeTab !== 'approved'}>
158
+ {_translations.tabTitleApprovedQuotas}
159
+ </span>
62
160
  <span hidden={activeTab !== 'sending_approval_request'}>
63
- Requires My Approval
161
+ {_translations.tabTitleRequiresMyApproval}
64
162
  </span>
65
- (<span>{filteredQuotations?.length}</span>)
163
+ (<span>{selectedQuotations?.count}</span>)
66
164
  </p>
67
165
 
68
166
  <table className="w-full">
@@ -82,7 +180,7 @@ export function AccountMyQuotations() {
82
180
  </tr>
83
181
  </thead>
84
182
  <tbody>
85
- {filteredQuotations?.map((quotation) => (
183
+ {selectedQuotations?.results?.map((quotation) => (
86
184
  <tr
87
185
  key={quotation.id}
88
186
  className="h-[64px] lg:h-[75px] even:bg-[#fbfcfd]"
@@ -122,11 +220,11 @@ export function AccountMyQuotations() {
122
220
  </table>
123
221
  </div>
124
222
 
125
- {filteredQuotations?.length > 0 && (
223
+ {selectedQuotations?.count > 0 && (
126
224
  <Pagination
127
- total={filteredQuotations.length}
225
+ total={selectedQuotations.count}
128
226
  limit={20}
129
- numberOfPages={Math.ceil(filteredQuotations.length / 20)}
227
+ numberOfPages={Math.ceil(selectedQuotations.count / 20)}
130
228
  containerClassName="lg:justify-end"
131
229
  currentPage={Number(searchParams.get('page')) || 1}
132
230
  />
@@ -1,6 +1,5 @@
1
1
  'use client';
2
2
 
3
- import { useLocalization } from '@akinon/next/hooks';
4
3
  import { Image, Price, Input, Icon } from '@akinon/next/components';
5
4
  import { useUpdateProductMutation } from '@akinon/next/data/client/b2b';
6
5
  import { BasketItemType } from '@akinon/next/types';
@@ -10,11 +9,12 @@ interface Props {
10
9
  basketItem: BasketItemType;
11
10
  setProductToBeDeleted: (item: BasketItemType) => void;
12
11
  setRemoveProduct: (open: boolean) => void;
12
+ deleteText: string;
13
13
  }
14
14
 
15
15
  export const BasketItem = (props: Props) => {
16
- const { t } = useLocalization();
17
- const { basketItem, setRemoveProduct, setProductToBeDeleted } = props;
16
+ const { basketItem, setRemoveProduct, setProductToBeDeleted, deleteText } =
17
+ props;
18
18
  const [updateProduct] = useUpdateProductMutation();
19
19
  const [divisionErrors, setDivisionErrors] = useState([]);
20
20
 
@@ -49,29 +49,41 @@ export const BasketItem = (props: Props) => {
49
49
  <>
50
50
  <tr>
51
51
  <td>
52
- <div className='h-[183px] w-full flex flex-col justify-between items-center text-xs'>
53
- <div className='w-full flex items-center justify-center py-3 mb-3 border-b'>
54
- <Icon name='chevron-down' size={10} />
52
+ <div className="h-[183px] w-full flex flex-col justify-between items-center text-xs">
53
+ <div className="w-full flex items-center justify-center py-3 mb-3 border-b">
54
+ <Icon name="chevron-down" size={10} />
55
55
  </div>
56
56
 
57
- <div className='h-full overflow-hidden'
58
- style={{ writingMode: 'vertical-rl', textOrientation: 'mixed', display: '-webkit-box', lineClamp: 1, WebkitLineClamp: 1, WebkitBoxOrient: 'vertical' }}
57
+ <div
58
+ className="h-full overflow-hidden"
59
+ style={{
60
+ writingMode: 'vertical-rl',
61
+ textOrientation: 'mixed',
62
+ display: '-webkit-box',
63
+ lineClamp: 1,
64
+ WebkitLineClamp: 1,
65
+ WebkitBoxOrient: 'vertical'
66
+ }}
59
67
  >
60
- {basketItem.product.name}{basketItem.product.name}{basketItem.product.name}
68
+ {basketItem.product.name}
69
+ {basketItem.product.name}
70
+ {basketItem.product.name}
61
71
  </div>
62
72
 
63
73
  <button
64
- onClick={() => { setProductToBeDeleted(basketItem), setRemoveProduct(true) }}
65
- className='py-1.5 border-t w-full flex items-center justify-center'
74
+ onClick={() => {
75
+ setProductToBeDeleted(basketItem), setRemoveProduct(true);
76
+ }}
77
+ className="py-1.5 border-t w-full flex items-center justify-center"
66
78
  style={{ writingMode: 'vertical-rl', textOrientation: 'mixed' }}
67
79
  >
68
- {t('basket.b2b.table.delete')}
80
+ {deleteText}
69
81
  </button>
70
82
  </div>
71
83
  </td>
72
84
 
73
- <td className='border text-center'>
74
- <div className='min-w-[80px] w-full h-full flex items-center justify-center'>
85
+ <td className="border text-center">
86
+ <div className="min-w-[80px] w-full h-full flex items-center justify-center">
75
87
  <Image
76
88
  src={basketItem.product.product_image}
77
89
  alt={basketItem.product.name}
@@ -81,30 +93,39 @@ export const BasketItem = (props: Props) => {
81
93
  </div>
82
94
  </td>
83
95
 
84
- <td className='min-w-[181px] text-sm border text-[#3f4b5c] leading-normal'>
85
- <div className='px-4'>
86
- <div className='break-words'>{basketItem.product.name}</div>
87
- <div className='break-words'>SKU: {basketItem.product.sku}</div>
96
+ <td className="min-w-[181px] text-sm border text-[#3f4b5c] leading-normal">
97
+ <div className="px-4">
98
+ <div className="break-words">{basketItem.product.name}</div>
99
+ <div className="break-words">SKU: {basketItem.product.sku}</div>
88
100
  </div>
89
101
  </td>
90
102
 
91
- <td className='text-sm border text-[#3f4b5c] leading-normal text-right px-3'>
92
- <Price value={basketItem.price} className='w-max block' />
103
+ <td className="text-sm border text-[#3f4b5c] leading-normal text-right px-3">
104
+ <Price value={basketItem.price} className="w-max block" />
93
105
  </td>
94
106
 
95
- <td className='text-sm border text-[#3f4b5c] leading-normal text-center'>
96
- <div className='min-w-[90px]'>
97
- {basketItem.quantity}
98
- </div>
107
+ <td className="text-sm border text-[#3f4b5c] leading-normal text-center">
108
+ <div className="min-w-[90px]">{basketItem.quantity}</div>
99
109
  </td>
100
110
 
101
- <td className='border'>
102
- <div className='min-w-[140px]'>
111
+ <td className="border">
112
+ <div className="min-w-[140px]">
103
113
  {basketItem.divisions.map((division) => (
104
- <div className='py-5 px-1 border-b last:border-0 text-center flex items-center justify-center gap-x-1' key={division.name}>
105
- <Icon name='b2b-info' size={18} className='text-[#293245]' />
106
-
107
- <div className='text-sm underline text-[#3f4b5c] overflow-hidden' style={{ display: '-webkit-box', lineClamp: 1, WebkitLineClamp: 1, WebkitBoxOrient: 'vertical' }}>
114
+ <div
115
+ className="py-5 px-1 border-b last:border-0 text-center flex items-center justify-center gap-x-1"
116
+ key={division.name}
117
+ >
118
+ <Icon name="b2b-info" size={18} className="text-[#293245]" />
119
+
120
+ <div
121
+ className="text-sm underline text-[#3f4b5c] overflow-hidden"
122
+ style={{
123
+ display: '-webkit-box',
124
+ lineClamp: 1,
125
+ WebkitLineClamp: 1,
126
+ WebkitBoxOrient: 'vertical'
127
+ }}
128
+ >
108
129
  {division.name}
109
130
  </div>
110
131
  </div>
@@ -112,12 +133,15 @@ export const BasketItem = (props: Props) => {
112
133
  </div>
113
134
  </td>
114
135
 
115
- <td className='border'>
116
- <div className='min-w-[120px]'>
136
+ <td className="border">
137
+ <div className="min-w-[120px]">
117
138
  {basketItem.divisions.map((division, index) => (
118
- <div className='relative py-5 border-b last:border-0 text-center flex flex-col gap-y-1 items-center justify-center' key={`division-${division.id}`}>
139
+ <div
140
+ className="relative py-5 border-b last:border-0 text-center flex flex-col gap-y-1 items-center justify-center"
141
+ key={`division-${division.id}`}
142
+ >
119
143
  <Input
120
- className='appearance-none outline-none w-20 h-6 px-2 py-0 border border-[#c8daec] rounded text-right'
144
+ className="appearance-none outline-none w-20 h-6 px-2 py-0 border border-[#c8daec] rounded text-right"
121
145
  type="number"
122
146
  value={division.quantity}
123
147
  onChange={(e) => handleChange(division.id, e, index)}
@@ -135,8 +159,8 @@ export const BasketItem = (props: Props) => {
135
159
  </div>
136
160
  </td>
137
161
 
138
- <td className='text-sm border text-[#3f4b5c] leading-normal text-right px-6'>
139
- <Price value={basketItem.total_amount} className='w-max block' />
162
+ <td className="text-sm border text-[#3f4b5c] leading-normal text-right px-6">
163
+ <Price value={basketItem.total_amount} className="w-max block" />
140
164
  </td>
141
165
  </tr>
142
166
  </>
@@ -2,7 +2,6 @@
2
2
 
3
3
  import { useDeleteProductMutation } from '@akinon/next/data/client/b2b';
4
4
  import { Image } from '@akinon/next/components';
5
- import { useLocalization } from '@akinon/next/hooks';
6
5
  import React, { useEffect } from 'react';
7
6
  import { BasketItemType } from '@akinon/next/types';
8
7
  import { useAddFavoriteMutation } from '@akinon/next/data/client/wishlist';
@@ -11,19 +10,26 @@ import clsx from 'clsx';
11
10
  export const RemoveProductModal = ({
12
11
  open,
13
12
  setOpen,
14
- basketItem
13
+ basketItem,
14
+ modalTitle,
15
+ modalDescription,
16
+ addToFavoritesButton,
17
+ deleteProductButton
15
18
  }: {
16
19
  open: boolean;
17
20
  setOpen: (open: boolean) => void;
18
- basketItem: BasketItemType
21
+ basketItem: BasketItemType;
22
+ modalTitle: string;
23
+ modalDescription: string;
24
+ addToFavoritesButton: string;
25
+ deleteProductButton: string;
19
26
  }) => {
20
- const { t } = useLocalization();
21
27
  const [addFavorite] = useAddFavoriteMutation();
22
28
  const [deleteProduct, { isLoading, isSuccess }] = useDeleteProductMutation();
23
29
 
24
- const handleClick = (addToFavorite:boolean) => {
25
- if(addToFavorite) {
26
- addFavorite(basketItem.product_remote_id)
30
+ const handleClick = (addToFavorite: boolean) => {
31
+ if (addToFavorite) {
32
+ addFavorite(basketItem.product_remote_id);
27
33
  }
28
34
 
29
35
  deleteProduct({ product_remote_id: basketItem.product_remote_id });
@@ -42,7 +48,7 @@ export const RemoveProductModal = ({
42
48
  <div className="w-11/12 max-w-[344px] md:w-[370px] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 fixed bg-white">
43
49
  <header className="relative border-b border-[#d5dadb]">
44
50
  <h3 className="text-left px-[30px] py-4 font-medium text-[17px]">
45
- {t('basket.b2b.remove_product_modal.title')}
51
+ {modalTitle}
46
52
  </h3>
47
53
 
48
54
  <button
@@ -53,22 +59,25 @@ export const RemoveProductModal = ({
53
59
  </button>
54
60
  </header>
55
61
 
56
- <p className="text-left px-[30px] py-4 text-sm">
57
- {t('basket.b2b.remove_product_modal.description')}
58
- </p>
62
+ <p className="text-left px-[30px] py-4 text-sm">{modalDescription}</p>
59
63
 
60
64
  <div className="px-[30px] pb-5">
61
- <div className='flex items-center gap-x-3'>
65
+ <div className="flex items-center gap-x-3">
62
66
  <div>
63
- <Image src={basketItem.product.product_image} width={72} height={93} alt={basketItem.product.name} />
67
+ <Image
68
+ src={basketItem.product.product_image}
69
+ width={72}
70
+ height={93}
71
+ alt={basketItem.product.name}
72
+ />
64
73
  </div>
65
- <div className='text-[#3f4b5c] text-sm'>
74
+ <div className="text-[#3f4b5c] text-sm">
66
75
  <p>{basketItem.product.name}</p>
67
76
  <p>SKU: {basketItem.product.sku}</p>
68
77
  </div>
69
78
  </div>
70
79
 
71
- <div className='flex gap-y-3 flex-col items-center'>
80
+ <div className="flex gap-y-3 flex-col items-center">
72
81
  <button
73
82
  type="submit"
74
83
  className="h-8 bg-black text-white font-medium text-sm w-full"
@@ -80,7 +89,7 @@ export const RemoveProductModal = ({
80
89
  <div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
81
90
  </div>
82
91
  ) : (
83
- t('basket.b2b.remove_product_modal.add_to_favorites_button')
92
+ addToFavoritesButton
84
93
  )}
85
94
  </button>
86
95
 
@@ -98,7 +107,7 @@ export const RemoveProductModal = ({
98
107
  <div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
99
108
  </div>
100
109
  ) : (
101
- t('basket.b2b.remove_product_modal.delete_product_button')
110
+ deleteProductButton
102
111
  )}
103
112
  </button>
104
113
  </div>
@@ -106,4 +115,4 @@ export const RemoveProductModal = ({
106
115
  </div>
107
116
  </div>
108
117
  );
109
- }
118
+ };
@@ -1,18 +1,23 @@
1
1
  'use client';
2
2
 
3
3
  import { useCreateQuotationMutation } from '@akinon/next/data/client/b2b';
4
- import { useLocalization } from '@akinon/next/hooks';
5
4
  import React, { useEffect, useState } from 'react';
6
5
 
7
6
  export const RequestQuoteModal = ({
8
7
  open,
9
- setOpen
8
+ setOpen,
9
+ modalTitle,
10
+ modalQuotationName,
11
+ modalButton
10
12
  }: {
11
13
  open: boolean;
12
14
  setOpen: (open: boolean) => void;
15
+ modalTitle: string;
16
+ modalQuotationName: string;
17
+ modalButton: string;
13
18
  }) => {
14
- const { t } = useLocalization();
15
- const [createQuotation, { isLoading, isSuccess, error, isError }] = useCreateQuotationMutation();
19
+ const [createQuotation, { isLoading, isSuccess, error, isError }] =
20
+ useCreateQuotationMutation();
16
21
  const [errors, setErrors] = useState([]);
17
22
  const [cartName, setCartName] = useState<string>('');
18
23
 
@@ -23,11 +28,11 @@ export const RequestQuoteModal = ({
23
28
  return;
24
29
  }
25
30
 
26
- createQuotation({ name: cartName }).then((res) => {
27
- if ('error' in res && 'data' in res.error) {
28
- setErrors((res.error.data as any).non_field_errors || []);
29
- }
30
- })
31
+ createQuotation({ name: cartName }).then((res) => {
32
+ if ('error' in res && 'data' in res.error) {
33
+ setErrors((res.error.data as any).non_field_errors || []);
34
+ }
35
+ });
31
36
  };
32
37
 
33
38
  useEffect(() => {
@@ -43,7 +48,7 @@ export const RequestQuoteModal = ({
43
48
  <div className="w-11/12 max-w-[344px] md:w-[370px] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 fixed bg-white">
44
49
  <header className="relative border-b border-[#d5dadb]">
45
50
  <h3 className="text-left px-[30px] py-4 font-medium text-lg">
46
- {t('basket.b2b.request_quote_modal.title')}
51
+ {modalTitle}
47
52
  </h3>
48
53
 
49
54
  <button
@@ -57,7 +62,7 @@ export const RequestQuoteModal = ({
57
62
  <div className="p-[30px]">
58
63
  <form onSubmit={(e) => handleSubmit(e)}>
59
64
  <label htmlFor="cart-name-input" className="text-xs block mb-2">
60
- {t('basket.b2b.request_quote_modal.quotation_name')}
65
+ {modalQuotationName}
61
66
  </label>
62
67
 
63
68
  <input
@@ -86,7 +91,7 @@ export const RequestQuoteModal = ({
86
91
  <div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
87
92
  </div>
88
93
  ) : (
89
- t('basket.b2b.request_quote_modal.button')
94
+ modalButton
90
95
  )}
91
96
  </button>
92
97
  </form>
@@ -94,4 +99,4 @@ export const RequestQuoteModal = ({
94
99
  </div>
95
100
  </div>
96
101
  );
97
- }
102
+ };
@@ -1,17 +1,21 @@
1
1
  'use client';
2
2
 
3
3
  import { useSaveBasketMutation } from '@akinon/next/data/client/b2b';
4
- import { useLocalization } from '@akinon/next/hooks';
5
4
  import React, { useEffect, useState } from 'react';
6
5
 
7
6
  export const SaveCartModal = ({
8
7
  open,
9
- setOpen
8
+ setOpen,
9
+ modalTitle,
10
+ modalCartName,
11
+ saveCartButton
10
12
  }: {
11
13
  open: boolean;
12
14
  setOpen: (open: boolean) => void;
15
+ modalTitle: string;
16
+ modalCartName: string;
17
+ saveCartButton: string;
13
18
  }) => {
14
- const { t } = useLocalization();
15
19
  const [cartName, setCartName] = useState<string>('');
16
20
  const [saveBasket, { isLoading, isSuccess }] = useSaveBasketMutation();
17
21
 
@@ -38,7 +42,7 @@ export const SaveCartModal = ({
38
42
  <div className="w-11/12 max-w-[344px] md:w-[370px] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 fixed bg-white">
39
43
  <header className="relative border-b border-[#d5dadb]">
40
44
  <h3 className="text-left px-[30px] py-4 font-medium text-lg">
41
- {t('basket.b2b.save_cart_modal.title')}
45
+ {modalTitle}
42
46
  </h3>
43
47
  <button
44
48
  className="absolute right-[30px] top-5"
@@ -50,7 +54,7 @@ export const SaveCartModal = ({
50
54
  <div className="p-[30px]">
51
55
  <form onSubmit={(e) => handleSubmit(e)}>
52
56
  <label htmlFor="cart-name-input" className="text-xs block mb-2">
53
- {t('basket.b2b.save_cart_modal.cart_name')}
57
+ {modalCartName}
54
58
  </label>
55
59
  <input
56
60
  id="cart-name-input"
@@ -69,7 +73,7 @@ export const SaveCartModal = ({
69
73
  <div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
70
74
  </div>
71
75
  ) : (
72
- t('basket.b2b.save_cart_modal.save_cart_button')
76
+ saveCartButton
73
77
  )}
74
78
  </button>
75
79
  </form>
@@ -77,4 +81,4 @@ export const SaveCartModal = ({
77
81
  </div>
78
82
  </div>
79
83
  );
80
- }
84
+ };
@@ -1,57 +1,38 @@
1
1
  'use client';
2
2
 
3
- import { GetQuotationsResponse } from '@akinon/next/data/client/account';
4
3
  import clsx from 'clsx';
5
- import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
4
+ import React, { Dispatch, SetStateAction } from 'react';
6
5
  import { twMerge } from 'tailwind-merge';
7
6
 
8
- type TabsType = {
9
- quotations: GetQuotationsResponse | undefined;
7
+ type TabsPropsType = {
8
+ quotationsCount: number;
9
+ pendingCount: number;
10
+ rejectedCount: number;
11
+ approvedCount: number;
12
+ sendingApprovalRequestCount: number;
10
13
  setActiveTab: Dispatch<SetStateAction<string>>;
11
14
  activeTab: string;
15
+ tabAllQuotas: string;
16
+ tabPendingQuotas: string;
17
+ tabRejectedQuotas: string;
18
+ tabApprovedQuotas: string;
19
+ tabRequiresMyApproval: string;
12
20
  };
13
21
 
14
- const initialTabCountsState = {
15
- pendingCount: 0,
16
- rejectedCount: 0,
17
- approvedCount: 0,
18
- sendingApprovalRequestCount: 0
19
- };
20
-
21
- export function Tabs(props: TabsType) {
22
- const quotations = props.quotations?.results;
23
- const setActiveTab = props.setActiveTab;
24
- const activeTab = props.activeTab;
25
- const [tabCounts, setTabCounts] = useState(initialTabCountsState);
26
-
27
- useEffect(() => {
28
- setTabCounts(initialTabCountsState);
29
-
30
- quotations?.forEach((quotation) => {
31
- if (quotation.status === 'pending') {
32
- setTabCounts((prevCount) => ({
33
- ...prevCount,
34
- pendingCount: prevCount.pendingCount + 1
35
- }));
36
- } else if (quotation.status === 'rejected') {
37
- setTabCounts((prevCount) => ({
38
- ...prevCount,
39
- rejectedCount: prevCount.rejectedCount + 1
40
- }));
41
- } else if (quotation.status === 'approved') {
42
- setTabCounts((prevCount) => ({
43
- ...prevCount,
44
- approvedCount: prevCount.approvedCount + 1
45
- }));
46
- } else if (quotation.status === 'sending_approval_request') {
47
- setTabCounts((prevCount) => ({
48
- ...prevCount,
49
- sendingApprovalRequestCount: prevCount.sendingApprovalRequestCount + 1
50
- }));
51
- }
52
- });
53
- }, [quotations]);
54
-
22
+ export function Tabs({
23
+ quotationsCount,
24
+ pendingCount,
25
+ rejectedCount,
26
+ approvedCount,
27
+ sendingApprovalRequestCount,
28
+ setActiveTab,
29
+ activeTab,
30
+ tabAllQuotas,
31
+ tabPendingQuotas,
32
+ tabRejectedQuotas,
33
+ tabApprovedQuotas,
34
+ tabRequiresMyApproval
35
+ }: TabsPropsType) {
55
36
  return (
56
37
  <div className="mb-9 mt-4 lg:mt-0">
57
38
  <div className="relative flex justify-start lg:justify-between space-x-3 lg:space-x-4">
@@ -75,7 +56,7 @@ export function Tabs(props: TabsType) {
75
56
  activeTab === 'all' && 'font-bold'
76
57
  )}
77
58
  >
78
- All Quotes (<span>{quotations?.length}</span>)
59
+ {tabAllQuotas} (<span>{quotationsCount}</span>)
79
60
  </span>
80
61
  </div>
81
62
 
@@ -99,7 +80,7 @@ export function Tabs(props: TabsType) {
99
80
  activeTab === 'pending' && 'font-bold'
100
81
  )}
101
82
  >
102
- Pending (<span>{tabCounts.pendingCount}</span>)
83
+ {tabPendingQuotas} (<span>{pendingCount}</span>)
103
84
  </span>
104
85
  </div>
105
86
 
@@ -123,7 +104,7 @@ export function Tabs(props: TabsType) {
123
104
  activeTab === 'rejected' && 'font-bold'
124
105
  )}
125
106
  >
126
- Rejected (<span>{tabCounts.rejectedCount}</span>)
107
+ {tabRejectedQuotas} (<span>{rejectedCount}</span>)
127
108
  </span>
128
109
  </div>
129
110
 
@@ -147,7 +128,7 @@ export function Tabs(props: TabsType) {
147
128
  activeTab === 'approved' && 'font-bold'
148
129
  )}
149
130
  >
150
- Approved (<span>{tabCounts.approvedCount}</span>)
131
+ {tabApprovedQuotas} (<span>{approvedCount}</span>)
151
132
  </span>
152
133
  </div>
153
134
 
@@ -172,8 +153,7 @@ export function Tabs(props: TabsType) {
172
153
  activeTab === 'sending_approval_request' && 'font-bold'
173
154
  )}
174
155
  >
175
- Requires My Approval (
176
- <span>{tabCounts.sendingApprovalRequestCount}</span>)
156
+ {tabRequiresMyApproval} (<span>{sendingApprovalRequestCount}</span>)
177
157
  </span>
178
158
  </div>
179
159
 
@@ -1,7 +1,6 @@
1
1
  'use client';
2
2
 
3
3
  import { useAddToBasketMutation } from '@akinon/next/data/client/b2b';
4
- import { useLocalization } from '@akinon/next/hooks';
5
4
  import { Division } from '@akinon/next/types';
6
5
  import React, { useMemo, useState } from 'react';
7
6
  import { twMerge } from 'tailwind-merge';
@@ -13,27 +12,51 @@ interface GetResponse<T> {
13
12
  results: T[];
14
13
  }
15
14
 
16
- export function StoreModal({
17
- storeData,
18
- productPk,
19
- open,
20
- setOpen
21
- }: {
15
+ interface StoreModalTranslationsProps {
16
+ modalTitle: string;
17
+ storeName: string;
18
+ quantity: string;
19
+ validQuantity: string;
20
+ addToBasket: string;
21
+ }
22
+
23
+ interface StoreModalProps {
22
24
  storeData: GetResponse<Division>;
23
25
  productPk: number;
24
26
  open: boolean;
25
27
  setOpen: (open: boolean) => void;
26
- }) {
28
+ translations?: Record<string, any>;
29
+ }
30
+
31
+ export function StoreModal({
32
+ storeData,
33
+ productPk,
34
+ open,
35
+ setOpen,
36
+ translations
37
+ }: StoreModalProps) {
27
38
  const initialStateSelectStore = {
28
39
  id: null,
29
40
  quantity: '0'
30
41
  };
31
42
 
32
- const { t } = useLocalization();
33
43
  const [addToBasket] = useAddToBasketMutation();
34
44
  const [selectedStore, setSelectedStore] = useState(initialStateSelectStore);
35
45
  const inputRefs = {};
36
46
 
47
+ const defaultTranslations: StoreModalTranslationsProps = {
48
+ modalTitle: 'Select Store',
49
+ storeName: 'Store Name',
50
+ quantity: 'Quantity',
51
+ validQuantity: 'Please enter a valid quantity',
52
+ addToBasket: 'Add to Basket'
53
+ };
54
+
55
+ const mergedTranslations = useMemo(
56
+ () => ({ ...defaultTranslations, ...translations }),
57
+ [translations]
58
+ );
59
+
37
60
  const handleRadioChange = (storeId) => {
38
61
  setSelectedStore((prevSelectedStore) => ({
39
62
  ...prevSelectedStore,
@@ -92,7 +115,7 @@ export function StoreModal({
92
115
  <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
116
  <header className="relative md:border-b border-[#d5dadb]">
94
117
  <h3 className="text-left px-4 md:text-center py-4 font-bold text-lg">
95
- {t('product.store_select_modal.title')}
118
+ {mergedTranslations.modalTitle}
96
119
  </h3>
97
120
  <button
98
121
  className="absolute right-[15px] md:right-[30px] top-5"
@@ -110,10 +133,10 @@ export function StoreModal({
110
133
  <tr className="border border-[#d7d7d7] bg-[#fafafa]">
111
134
  <th className="w-[43px] border-r border-[#d7d7d7]"></th>
112
135
  <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')}
136
+ {mergedTranslations.storeName}
114
137
  </th>
115
138
  <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')}
139
+ {mergedTranslations.quantity}
117
140
  </th>
118
141
  </tr>
119
142
  </thead>
@@ -150,7 +173,7 @@ export function StoreModal({
150
173
  </table>
151
174
 
152
175
  <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>
176
+ <p hidden>{mergedTranslations.validQuantity}</p>
154
177
 
155
178
  <button
156
179
  className={twMerge(
@@ -160,7 +183,7 @@ export function StoreModal({
160
183
  onClick={handleAddToBasket}
161
184
  disabled={isButtonDisabled}
162
185
  >
163
- {t('product.store_select_modal.add_to_basket')}
186
+ {mergedTranslations.addToBasket}
164
187
  </button>
165
188
  </div>
166
189
  </div>