@akinon/pz-b2b 1.18.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }