@akinon/pz-b2b 1.18.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.
@@ -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
+ }