@akinon/pz-b2b 1.19.4 → 1.20.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/.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,11 @@
1
1
  # @akinon/pz-b2b
2
2
 
3
+ ## 1.20.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 86a9561: ZERO-2416: Update translations and refactor my quotations
8
+
3
9
  ## 1.19.4
4
10
 
5
11
  ## 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.20.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>