bitsnap-payloadcms 0.0.0-stage → 0.1.2
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/LICENSE +21 -0
- package/README.md +270 -2
- package/dist/client.d.ts +3 -0
- package/dist/client.js +5 -0
- package/dist/client.js.map +1 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -0
- package/dist/fields/getFields.d.ts +54 -0
- package/dist/fields/getFields.js +150 -0
- package/dist/fields/getFields.js.map +1 -0
- package/dist/fields/index.d.ts +2 -0
- package/dist/fields/index.js +3 -0
- package/dist/fields/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +15 -0
- package/dist/plugin.js +60 -0
- package/dist/plugin.js.map +1 -0
- package/dist/server/client.d.ts +47 -0
- package/dist/server/client.js +68 -0
- package/dist/server/client.js.map +1 -0
- package/dist/server/endpoints.d.ts +9 -0
- package/dist/server/endpoints.js +190 -0
- package/dist/server/endpoints.js.map +1 -0
- package/dist/server/errors.d.ts +9 -0
- package/dist/server/errors.js +35 -0
- package/dist/server/errors.js.map +1 -0
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.js +6 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/schemas.d.ts +55 -0
- package/dist/server/schemas.js +46 -0
- package/dist/server/schemas.js.map +1 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/BitsnapManager.css +159 -0
- package/dist/ui/BitsnapManager.d.ts +5 -0
- package/dist/ui/BitsnapManager.js +642 -0
- package/dist/ui/BitsnapManager.js.map +1 -0
- package/dist/ui/LinkToDoc.d.ts +4 -0
- package/dist/ui/LinkToDoc.js +48 -0
- package/dist/ui/LinkToDoc.js.map +1 -0
- package/dist/ui/ProductSelect.css +199 -0
- package/dist/ui/ProductSelect.d.ts +5 -0
- package/dist/ui/ProductSelect.js +146 -0
- package/dist/ui/ProductSelect.js.map +1 -0
- package/dist/ui/getBitsnapClientConfig.d.ts +13 -0
- package/dist/ui/getBitsnapClientConfig.js +30 -0
- package/dist/ui/getBitsnapClientConfig.js.map +1 -0
- package/dist/utils/formatCurrency.d.ts +6 -0
- package/dist/utils/formatCurrency.js +24 -0
- package/dist/utils/formatCurrency.js.map +1 -0
- package/package.json +93 -4
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { Banner, Button, CopyToClipboard, FieldDescription, Pill, SelectInput, TextInput, useForm, useFormFields } from '@payloadcms/ui';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
5
|
+
import { getBitsnapClientConfig } from './getBitsnapClientConfig.js';
|
|
6
|
+
import './BitsnapManager.css';
|
|
7
|
+
const statusLabels = {
|
|
8
|
+
DRAFT: 'Szkic',
|
|
9
|
+
UNPUBLISHED: 'Nieopublikowany',
|
|
10
|
+
PUBLISHED: 'Opublikowany'
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Matches any field path that ends with `productID` — layout agnostic on purpose,
|
|
14
|
+
* the plugin must not depend on a specific block layout of the consuming app.
|
|
15
|
+
*/ const productIDPathPattern = /(?:^|\.)productID$/;
|
|
16
|
+
/**
|
|
17
|
+
* Extracts the numeric index of a block/array row out of a field path
|
|
18
|
+
* (`layout.2.productID` -> `2`). Paths without an index sort last.
|
|
19
|
+
*/ const getPathIndex = (path)=>{
|
|
20
|
+
const match = /(?:^|\.)(\d+)(?:\.|$)/.exec(path);
|
|
21
|
+
if (!match || match[1] === undefined) {
|
|
22
|
+
return Number.MAX_SAFE_INTEGER;
|
|
23
|
+
}
|
|
24
|
+
return Number(match[1]);
|
|
25
|
+
};
|
|
26
|
+
const getProductIDPaths = (fields)=>Object.keys(fields).filter((path)=>productIDPathPattern.test(path)).sort((a, b)=>{
|
|
27
|
+
const indexA = getPathIndex(a);
|
|
28
|
+
const indexB = getPathIndex(b);
|
|
29
|
+
if (indexA !== indexB) {
|
|
30
|
+
return indexA - indexB;
|
|
31
|
+
}
|
|
32
|
+
return a.localeCompare(b);
|
|
33
|
+
});
|
|
34
|
+
const groszeToZl = (grosze)=>grosze === null ? '' : (grosze / 100).toFixed(2).replace('.', ',');
|
|
35
|
+
const parsePrice = (value)=>{
|
|
36
|
+
const trimmed = value.trim();
|
|
37
|
+
if (trimmed === '') return undefined;
|
|
38
|
+
const normalized = trimmed.replace(/\s/g, '').replace(',', '.');
|
|
39
|
+
const parsed = Number.parseFloat(normalized);
|
|
40
|
+
if (!Number.isFinite(parsed) || parsed < 0) return null;
|
|
41
|
+
return Math.round(parsed * 100);
|
|
42
|
+
};
|
|
43
|
+
const parseQuantity = (value)=>{
|
|
44
|
+
const trimmed = value.trim();
|
|
45
|
+
if (trimmed === '') return undefined;
|
|
46
|
+
const parsed = Number(trimmed);
|
|
47
|
+
if (!Number.isInteger(parsed) || parsed < 0) return null;
|
|
48
|
+
return parsed;
|
|
49
|
+
};
|
|
50
|
+
export const BitsnapManager = (props)=>{
|
|
51
|
+
const { dispatchFields } = useForm();
|
|
52
|
+
const { apiBaseUrl, currency, dashboardUrl } = getBitsnapClientConfig(props);
|
|
53
|
+
const description = typeof props.field.admin?.description === 'string' ? props.field.admin.description : undefined;
|
|
54
|
+
const productIDPathsKey = useFormFields(([fields])=>getProductIDPaths(fields).join(','));
|
|
55
|
+
const productIDPaths = useMemo(()=>productIDPathsKey === '' ? [] : productIDPathsKey.split(','), [
|
|
56
|
+
productIDPathsKey
|
|
57
|
+
]);
|
|
58
|
+
const linkedProductID = useFormFields(([fields])=>{
|
|
59
|
+
for (const path of getProductIDPaths(fields)){
|
|
60
|
+
const value = fields[path]?.value;
|
|
61
|
+
if (typeof value === 'string' && value !== '') return value;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
});
|
|
65
|
+
const productTitle = useFormFields(([fields])=>{
|
|
66
|
+
const value = fields.title?.value;
|
|
67
|
+
return typeof value === 'string' ? value : '';
|
|
68
|
+
});
|
|
69
|
+
const [status, setStatus] = useState('DRAFT');
|
|
70
|
+
const [price, setPrice] = useState('');
|
|
71
|
+
const [stock, setStock] = useState('');
|
|
72
|
+
const [variants, setVariants] = useState([]);
|
|
73
|
+
const [revision, setRevision] = useState(null);
|
|
74
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
75
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
76
|
+
const [message, setMessage] = useState(null);
|
|
77
|
+
const [newName, setNewName] = useState(null);
|
|
78
|
+
const [newPrice, setNewPrice] = useState('');
|
|
79
|
+
const [newStatus, setNewStatus] = useState('DRAFT');
|
|
80
|
+
const [isCreating, setIsCreating] = useState(false);
|
|
81
|
+
const [createdProductID, setCreatedProductID] = useState(null);
|
|
82
|
+
const applyProduct = useCallback((product)=>{
|
|
83
|
+
setStatus(product.status);
|
|
84
|
+
setPrice(groszeToZl(product.priceAmount));
|
|
85
|
+
setStock(product.availableQuantity === null ? '' : String(product.availableQuantity));
|
|
86
|
+
setRevision(product.revision);
|
|
87
|
+
setVariants(product.variants.map((variant)=>({
|
|
88
|
+
id: variant.id,
|
|
89
|
+
name: variant.name,
|
|
90
|
+
price: groszeToZl(variant.priceAmount),
|
|
91
|
+
stock: variant.availableQuantity === null ? '' : String(variant.availableQuantity)
|
|
92
|
+
})));
|
|
93
|
+
}, []);
|
|
94
|
+
const load = useCallback(async ()=>{
|
|
95
|
+
if (!linkedProductID) {
|
|
96
|
+
setRevision(null);
|
|
97
|
+
setVariants([]);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
setIsLoading(true);
|
|
101
|
+
setMessage(null);
|
|
102
|
+
try {
|
|
103
|
+
const response = await fetch(`${apiBaseUrl}/products/${encodeURIComponent(linkedProductID)}`);
|
|
104
|
+
const payload = await response.json();
|
|
105
|
+
if (!response.ok || !payload.product) {
|
|
106
|
+
throw new Error(payload.error ?? 'Nie udało się pobrać produktu z Bitsnapa');
|
|
107
|
+
}
|
|
108
|
+
applyProduct(payload.product);
|
|
109
|
+
return true;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
setMessage({
|
|
112
|
+
tone: 'error',
|
|
113
|
+
text: error instanceof Error ? error.message : 'Nie udało się pobrać produktu z Bitsnapa'
|
|
114
|
+
});
|
|
115
|
+
return false;
|
|
116
|
+
} finally{
|
|
117
|
+
setIsLoading(false);
|
|
118
|
+
}
|
|
119
|
+
}, [
|
|
120
|
+
apiBaseUrl,
|
|
121
|
+
applyProduct,
|
|
122
|
+
linkedProductID
|
|
123
|
+
]);
|
|
124
|
+
useEffect(()=>{
|
|
125
|
+
void load();
|
|
126
|
+
}, [
|
|
127
|
+
load
|
|
128
|
+
]);
|
|
129
|
+
const save = useCallback(async ()=>{
|
|
130
|
+
if (!linkedProductID || revision === null) return;
|
|
131
|
+
const priceAmount = parsePrice(price);
|
|
132
|
+
const availableQuantity = parseQuantity(stock);
|
|
133
|
+
if (priceAmount === null) {
|
|
134
|
+
setMessage({
|
|
135
|
+
tone: 'error',
|
|
136
|
+
text: 'Cena bazowa musi być liczbą'
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (availableQuantity === null) {
|
|
141
|
+
setMessage({
|
|
142
|
+
tone: 'error',
|
|
143
|
+
text: 'Limit sztuk musi być liczbą'
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const payloadVariants = [];
|
|
148
|
+
for (const variant of variants){
|
|
149
|
+
const name = variant.name.trim();
|
|
150
|
+
const variantPrice = parsePrice(variant.price);
|
|
151
|
+
const variantStock = parseQuantity(variant.stock);
|
|
152
|
+
if (name === '') {
|
|
153
|
+
setMessage({
|
|
154
|
+
tone: 'error',
|
|
155
|
+
text: 'Nazwa wariantu jest wymagana'
|
|
156
|
+
});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (variantPrice === null || variantPrice === undefined) {
|
|
160
|
+
setMessage({
|
|
161
|
+
tone: 'error',
|
|
162
|
+
text: `Cena wariantu „${name}” jest wymagana`
|
|
163
|
+
});
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (variantStock === null) {
|
|
167
|
+
setMessage({
|
|
168
|
+
tone: 'error',
|
|
169
|
+
text: `Limit sztuk wariantu „${name}” musi być liczbą`
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
payloadVariants.push({
|
|
174
|
+
...variant.id ? {
|
|
175
|
+
id: variant.id
|
|
176
|
+
} : {},
|
|
177
|
+
name,
|
|
178
|
+
priceAmount: variantPrice,
|
|
179
|
+
priceCurrency: currency,
|
|
180
|
+
...variantStock === undefined ? {} : {
|
|
181
|
+
availableQuantity: variantStock
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
setIsSaving(true);
|
|
186
|
+
setMessage(null);
|
|
187
|
+
try {
|
|
188
|
+
const response = await fetch(`${apiBaseUrl}/products/${encodeURIComponent(linkedProductID)}`, {
|
|
189
|
+
body: JSON.stringify({
|
|
190
|
+
revision,
|
|
191
|
+
status,
|
|
192
|
+
...priceAmount === undefined ? {} : {
|
|
193
|
+
priceAmount
|
|
194
|
+
},
|
|
195
|
+
...availableQuantity === undefined ? {} : {
|
|
196
|
+
availableQuantity
|
|
197
|
+
},
|
|
198
|
+
variants: payloadVariants
|
|
199
|
+
}),
|
|
200
|
+
headers: {
|
|
201
|
+
'Content-Type': 'application/json'
|
|
202
|
+
},
|
|
203
|
+
method: 'PATCH'
|
|
204
|
+
});
|
|
205
|
+
const payload = await response.json();
|
|
206
|
+
if (!response.ok) {
|
|
207
|
+
if (response.status === 409) {
|
|
208
|
+
setMessage({
|
|
209
|
+
tone: 'error',
|
|
210
|
+
text: 'Produkt zmienił się w Bitsnap — kliknij Odśwież'
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
throw new Error(payload.error ?? 'Nie udało się zapisać produktu w Bitsnapie');
|
|
215
|
+
}
|
|
216
|
+
const reloaded = await load();
|
|
217
|
+
if (reloaded) {
|
|
218
|
+
setMessage({
|
|
219
|
+
tone: 'success',
|
|
220
|
+
text: 'Zapisano w Bitsnapie'
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
setMessage({
|
|
225
|
+
tone: 'error',
|
|
226
|
+
text: error instanceof Error ? error.message : 'Nie udało się zapisać produktu w Bitsnapie'
|
|
227
|
+
});
|
|
228
|
+
} finally{
|
|
229
|
+
setIsSaving(false);
|
|
230
|
+
}
|
|
231
|
+
}, [
|
|
232
|
+
apiBaseUrl,
|
|
233
|
+
currency,
|
|
234
|
+
linkedProductID,
|
|
235
|
+
load,
|
|
236
|
+
price,
|
|
237
|
+
revision,
|
|
238
|
+
status,
|
|
239
|
+
stock,
|
|
240
|
+
variants
|
|
241
|
+
]);
|
|
242
|
+
const create = useCallback(async ()=>{
|
|
243
|
+
const name = (newName ?? productTitle).trim();
|
|
244
|
+
const priceAmount = parsePrice(newPrice);
|
|
245
|
+
if (name === '') {
|
|
246
|
+
setMessage({
|
|
247
|
+
tone: 'error',
|
|
248
|
+
text: 'Podaj nazwę produktu'
|
|
249
|
+
});
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (priceAmount === null || priceAmount === undefined) {
|
|
253
|
+
setMessage({
|
|
254
|
+
tone: 'error',
|
|
255
|
+
text: 'Podaj cenę produktu w zł'
|
|
256
|
+
});
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
setIsCreating(true);
|
|
260
|
+
setMessage(null);
|
|
261
|
+
try {
|
|
262
|
+
const response = await fetch(`${apiBaseUrl}/products`, {
|
|
263
|
+
body: JSON.stringify({
|
|
264
|
+
name,
|
|
265
|
+
priceAmount,
|
|
266
|
+
priceCurrency: currency,
|
|
267
|
+
status: newStatus
|
|
268
|
+
}),
|
|
269
|
+
headers: {
|
|
270
|
+
'Content-Type': 'application/json'
|
|
271
|
+
},
|
|
272
|
+
method: 'POST'
|
|
273
|
+
});
|
|
274
|
+
const payload = await response.json();
|
|
275
|
+
if (!response.ok || !payload.id) {
|
|
276
|
+
throw new Error(payload.error ?? 'Nie udało się utworzyć produktu w Bitsnapie');
|
|
277
|
+
}
|
|
278
|
+
setCreatedProductID(payload.id);
|
|
279
|
+
const targetPath = productIDPaths[0];
|
|
280
|
+
if (targetPath) {
|
|
281
|
+
dispatchFields({
|
|
282
|
+
path: targetPath,
|
|
283
|
+
type: 'UPDATE',
|
|
284
|
+
value: payload.id
|
|
285
|
+
});
|
|
286
|
+
setMessage({
|
|
287
|
+
tone: 'success',
|
|
288
|
+
text: `Utworzono produkt ${payload.id} i przypisano go do bloku produktu`
|
|
289
|
+
});
|
|
290
|
+
} else {
|
|
291
|
+
setMessage({
|
|
292
|
+
tone: 'success',
|
|
293
|
+
text: `Utworzono produkt ${payload.id}. Dodaj blok produktu i wybierz ten produkt.`
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
} catch (error) {
|
|
297
|
+
setMessage({
|
|
298
|
+
tone: 'error',
|
|
299
|
+
text: error instanceof Error ? error.message : 'Nie udało się utworzyć produktu w Bitsnapie'
|
|
300
|
+
});
|
|
301
|
+
} finally{
|
|
302
|
+
setIsCreating(false);
|
|
303
|
+
}
|
|
304
|
+
}, [
|
|
305
|
+
apiBaseUrl,
|
|
306
|
+
currency,
|
|
307
|
+
dispatchFields,
|
|
308
|
+
newName,
|
|
309
|
+
newPrice,
|
|
310
|
+
newStatus,
|
|
311
|
+
productIDPaths,
|
|
312
|
+
productTitle
|
|
313
|
+
]);
|
|
314
|
+
const updateVariant = (index, patch)=>{
|
|
315
|
+
setVariants((current)=>current.map((variant, variantIndex)=>variantIndex === index ? {
|
|
316
|
+
...variant,
|
|
317
|
+
...patch
|
|
318
|
+
} : variant));
|
|
319
|
+
};
|
|
320
|
+
const statusOptions = Object.keys(statusLabels).map((option)=>({
|
|
321
|
+
label: statusLabels[option],
|
|
322
|
+
value: option
|
|
323
|
+
}));
|
|
324
|
+
const renderStatusSelect = (value, onChange, path)=>/*#__PURE__*/ _jsx(SelectInput, {
|
|
325
|
+
isClearable: false,
|
|
326
|
+
label: "Status",
|
|
327
|
+
name: path,
|
|
328
|
+
onChange: (option)=>{
|
|
329
|
+
const next = Array.isArray(option) ? undefined : option?.value;
|
|
330
|
+
if (next === 'DRAFT' || next === 'UNPUBLISHED' || next === 'PUBLISHED') {
|
|
331
|
+
onChange(next);
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
options: statusOptions,
|
|
335
|
+
path: path,
|
|
336
|
+
value: value
|
|
337
|
+
});
|
|
338
|
+
const header = /*#__PURE__*/ _jsx("div", {
|
|
339
|
+
className: "group-field__header",
|
|
340
|
+
children: /*#__PURE__*/ _jsxs("header", {
|
|
341
|
+
children: [
|
|
342
|
+
/*#__PURE__*/ _jsx("h3", {
|
|
343
|
+
className: "group-field__title",
|
|
344
|
+
children: typeof props.field.label === 'string' ? props.field.label : 'Bitsnap'
|
|
345
|
+
}),
|
|
346
|
+
/*#__PURE__*/ _jsx(FieldDescription, {
|
|
347
|
+
path: props.path,
|
|
348
|
+
description: description
|
|
349
|
+
})
|
|
350
|
+
]
|
|
351
|
+
})
|
|
352
|
+
});
|
|
353
|
+
const banner = message && /*#__PURE__*/ _jsx(Banner, {
|
|
354
|
+
type: message.tone === 'error' ? 'error' : 'success',
|
|
355
|
+
children: message.text
|
|
356
|
+
});
|
|
357
|
+
if (!linkedProductID) {
|
|
358
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
359
|
+
className: "group-field group-field--top-level bitsnap-manager",
|
|
360
|
+
children: [
|
|
361
|
+
header,
|
|
362
|
+
/*#__PURE__*/ _jsx("p", {
|
|
363
|
+
className: "bitsnap-manager__lead",
|
|
364
|
+
children: "Ten produkt nie jest jeszcze powiązany z Bitsnapem. Utwórz go tutaj — id trafi do bloku produktu."
|
|
365
|
+
}),
|
|
366
|
+
banner,
|
|
367
|
+
/*#__PURE__*/ _jsx("div", {
|
|
368
|
+
className: "field-type row",
|
|
369
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
370
|
+
className: "row__fields",
|
|
371
|
+
children: [
|
|
372
|
+
/*#__PURE__*/ _jsx("div", {
|
|
373
|
+
className: "bitsnap-manager__col--half",
|
|
374
|
+
children: /*#__PURE__*/ _jsx(TextInput, {
|
|
375
|
+
label: "Nazwa",
|
|
376
|
+
onChange: (event)=>setNewName(event.target.value),
|
|
377
|
+
path: "bitsnapManager.newName",
|
|
378
|
+
value: newName ?? productTitle
|
|
379
|
+
})
|
|
380
|
+
}),
|
|
381
|
+
/*#__PURE__*/ _jsx("div", {
|
|
382
|
+
className: "bitsnap-manager__col--quarter",
|
|
383
|
+
children: /*#__PURE__*/ _jsx(TextInput, {
|
|
384
|
+
label: "Cena (zł)",
|
|
385
|
+
onChange: (event)=>setNewPrice(event.target.value),
|
|
386
|
+
path: "bitsnapManager.newPrice",
|
|
387
|
+
placeholder: "49,00",
|
|
388
|
+
value: newPrice
|
|
389
|
+
})
|
|
390
|
+
}),
|
|
391
|
+
/*#__PURE__*/ _jsx("div", {
|
|
392
|
+
className: "bitsnap-manager__col--quarter",
|
|
393
|
+
children: renderStatusSelect(newStatus, setNewStatus, 'bitsnapManager.newStatus')
|
|
394
|
+
})
|
|
395
|
+
]
|
|
396
|
+
})
|
|
397
|
+
}),
|
|
398
|
+
createdProductID && productIDPaths.length === 0 && /*#__PURE__*/ _jsxs(Banner, {
|
|
399
|
+
type: "info",
|
|
400
|
+
children: [
|
|
401
|
+
"Dodaj blok produktu (np. Zdjęcie produktu) i wybierz w nim produkt ",
|
|
402
|
+
createdProductID,
|
|
403
|
+
"."
|
|
404
|
+
]
|
|
405
|
+
}),
|
|
406
|
+
/*#__PURE__*/ _jsx("div", {
|
|
407
|
+
className: "bitsnap-manager__actions",
|
|
408
|
+
children: /*#__PURE__*/ _jsx(Button, {
|
|
409
|
+
buttonStyle: "primary",
|
|
410
|
+
disabled: isCreating,
|
|
411
|
+
onClick: ()=>{
|
|
412
|
+
void create();
|
|
413
|
+
},
|
|
414
|
+
children: isCreating ? 'Tworzenie…' : 'Utwórz produkt w Bitsnap'
|
|
415
|
+
})
|
|
416
|
+
})
|
|
417
|
+
]
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
const isInitialLoad = isLoading && revision === null;
|
|
421
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
422
|
+
className: "group-field group-field--top-level bitsnap-manager",
|
|
423
|
+
children: [
|
|
424
|
+
header,
|
|
425
|
+
banner,
|
|
426
|
+
isInitialLoad ? /*#__PURE__*/ _jsxs("div", {
|
|
427
|
+
className: "bitsnap-manager__loading",
|
|
428
|
+
children: [
|
|
429
|
+
/*#__PURE__*/ _jsx("div", {
|
|
430
|
+
className: "bitsnap-manager__loading-row"
|
|
431
|
+
}),
|
|
432
|
+
/*#__PURE__*/ _jsx("div", {
|
|
433
|
+
className: "bitsnap-manager__loading-row"
|
|
434
|
+
})
|
|
435
|
+
]
|
|
436
|
+
}) : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
437
|
+
children: [
|
|
438
|
+
/*#__PURE__*/ _jsx("div", {
|
|
439
|
+
className: "field-type row",
|
|
440
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
441
|
+
className: "row__fields",
|
|
442
|
+
children: [
|
|
443
|
+
/*#__PURE__*/ _jsx("div", {
|
|
444
|
+
className: "bitsnap-manager__col--third",
|
|
445
|
+
children: renderStatusSelect(status, setStatus, 'bitsnapManager.status')
|
|
446
|
+
}),
|
|
447
|
+
/*#__PURE__*/ _jsx("div", {
|
|
448
|
+
className: "bitsnap-manager__col--third",
|
|
449
|
+
children: /*#__PURE__*/ _jsx(TextInput, {
|
|
450
|
+
label: "Cena bazowa (zł)",
|
|
451
|
+
onChange: (event)=>setPrice(event.target.value),
|
|
452
|
+
path: "bitsnapManager.price",
|
|
453
|
+
placeholder: "49,00",
|
|
454
|
+
value: price
|
|
455
|
+
})
|
|
456
|
+
}),
|
|
457
|
+
/*#__PURE__*/ _jsx("div", {
|
|
458
|
+
className: "bitsnap-manager__col--third",
|
|
459
|
+
children: /*#__PURE__*/ _jsx(TextInput, {
|
|
460
|
+
description: "Sprzedane i zarezerwowane sztuki są od niego odejmowane. Puste pole — bez zmian.",
|
|
461
|
+
label: "Limit sztuk (łącznie)",
|
|
462
|
+
onChange: (event)=>setStock(event.target.value),
|
|
463
|
+
path: "bitsnapManager.stock",
|
|
464
|
+
placeholder: "np. 100",
|
|
465
|
+
value: stock
|
|
466
|
+
})
|
|
467
|
+
})
|
|
468
|
+
]
|
|
469
|
+
})
|
|
470
|
+
}),
|
|
471
|
+
/*#__PURE__*/ _jsxs("section", {
|
|
472
|
+
"aria-labelledby": "bitsnap-variants-title",
|
|
473
|
+
className: "bitsnap-manager__variants",
|
|
474
|
+
children: [
|
|
475
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
476
|
+
className: "bitsnap-manager__variants-header",
|
|
477
|
+
children: [
|
|
478
|
+
/*#__PURE__*/ _jsx("h4", {
|
|
479
|
+
className: "bitsnap-manager__variants-title",
|
|
480
|
+
id: "bitsnap-variants-title",
|
|
481
|
+
children: "Warianty"
|
|
482
|
+
}),
|
|
483
|
+
/*#__PURE__*/ _jsx(Pill, {
|
|
484
|
+
size: "small",
|
|
485
|
+
children: variants.length
|
|
486
|
+
})
|
|
487
|
+
]
|
|
488
|
+
}),
|
|
489
|
+
variants.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
490
|
+
className: "bitsnap-manager__empty",
|
|
491
|
+
children: "Brak wariantów — produkt sprzedaje się w jednej wersji."
|
|
492
|
+
}) : /*#__PURE__*/ _jsxs("div", {
|
|
493
|
+
className: "bitsnap-manager__grid",
|
|
494
|
+
role: "table",
|
|
495
|
+
"aria-label": "Warianty",
|
|
496
|
+
children: [
|
|
497
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
498
|
+
className: "bitsnap-manager__grid-head",
|
|
499
|
+
role: "row",
|
|
500
|
+
children: [
|
|
501
|
+
/*#__PURE__*/ _jsx("span", {
|
|
502
|
+
className: "field-label",
|
|
503
|
+
role: "columnheader",
|
|
504
|
+
children: "Nazwa"
|
|
505
|
+
}),
|
|
506
|
+
/*#__PURE__*/ _jsx("span", {
|
|
507
|
+
className: "field-label",
|
|
508
|
+
role: "columnheader",
|
|
509
|
+
children: "Cena (zł)"
|
|
510
|
+
}),
|
|
511
|
+
/*#__PURE__*/ _jsx("span", {
|
|
512
|
+
className: "field-label",
|
|
513
|
+
role: "columnheader",
|
|
514
|
+
children: "Limit sztuk"
|
|
515
|
+
}),
|
|
516
|
+
/*#__PURE__*/ _jsx("span", {
|
|
517
|
+
role: "columnheader",
|
|
518
|
+
className: "bitsnap-manager__sr-only",
|
|
519
|
+
children: "Akcje"
|
|
520
|
+
})
|
|
521
|
+
]
|
|
522
|
+
}),
|
|
523
|
+
variants.map((variant, index)=>/*#__PURE__*/ _jsxs("div", {
|
|
524
|
+
className: "bitsnap-manager__grid-row",
|
|
525
|
+
role: "row",
|
|
526
|
+
children: [
|
|
527
|
+
/*#__PURE__*/ _jsx("div", {
|
|
528
|
+
role: "cell",
|
|
529
|
+
children: /*#__PURE__*/ _jsx(TextInput, {
|
|
530
|
+
onChange: (event)=>updateVariant(index, {
|
|
531
|
+
name: event.target.value
|
|
532
|
+
}),
|
|
533
|
+
path: `bitsnapManager.variants.${index}.name`,
|
|
534
|
+
placeholder: `Wariant ${index + 1}, np. M`,
|
|
535
|
+
value: variant.name
|
|
536
|
+
})
|
|
537
|
+
}),
|
|
538
|
+
/*#__PURE__*/ _jsx("div", {
|
|
539
|
+
role: "cell",
|
|
540
|
+
children: /*#__PURE__*/ _jsx(TextInput, {
|
|
541
|
+
onChange: (event)=>updateVariant(index, {
|
|
542
|
+
price: event.target.value
|
|
543
|
+
}),
|
|
544
|
+
path: `bitsnapManager.variants.${index}.price`,
|
|
545
|
+
placeholder: "49,00",
|
|
546
|
+
value: variant.price
|
|
547
|
+
})
|
|
548
|
+
}),
|
|
549
|
+
/*#__PURE__*/ _jsx("div", {
|
|
550
|
+
role: "cell",
|
|
551
|
+
children: /*#__PURE__*/ _jsx(TextInput, {
|
|
552
|
+
onChange: (event)=>updateVariant(index, {
|
|
553
|
+
stock: event.target.value
|
|
554
|
+
}),
|
|
555
|
+
path: `bitsnapManager.variants.${index}.stock`,
|
|
556
|
+
placeholder: "np. 10",
|
|
557
|
+
value: variant.stock
|
|
558
|
+
})
|
|
559
|
+
}),
|
|
560
|
+
/*#__PURE__*/ _jsx("div", {
|
|
561
|
+
role: "cell",
|
|
562
|
+
className: "bitsnap-manager__remove",
|
|
563
|
+
children: /*#__PURE__*/ _jsx(Button, {
|
|
564
|
+
"aria-label": `Usuń wariant ${variant.name || index + 1}`,
|
|
565
|
+
buttonStyle: "icon-label",
|
|
566
|
+
icon: "x",
|
|
567
|
+
onClick: ()=>setVariants((current)=>current.filter((_, i)=>i !== index)),
|
|
568
|
+
round: true,
|
|
569
|
+
tooltip: "Usuń wariant"
|
|
570
|
+
})
|
|
571
|
+
})
|
|
572
|
+
]
|
|
573
|
+
}, variant.id ?? `new-${index}`))
|
|
574
|
+
]
|
|
575
|
+
}),
|
|
576
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
577
|
+
buttonStyle: "icon-label",
|
|
578
|
+
icon: "plus",
|
|
579
|
+
iconPosition: "left",
|
|
580
|
+
iconStyle: "with-border",
|
|
581
|
+
onClick: ()=>setVariants((current)=>[
|
|
582
|
+
...current,
|
|
583
|
+
{
|
|
584
|
+
name: '',
|
|
585
|
+
price: '',
|
|
586
|
+
stock: ''
|
|
587
|
+
}
|
|
588
|
+
]),
|
|
589
|
+
children: "Dodaj wariant"
|
|
590
|
+
})
|
|
591
|
+
]
|
|
592
|
+
})
|
|
593
|
+
]
|
|
594
|
+
}),
|
|
595
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
596
|
+
className: "bitsnap-manager__actions",
|
|
597
|
+
children: [
|
|
598
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
599
|
+
buttonStyle: "primary",
|
|
600
|
+
disabled: isSaving || isLoading || revision === null,
|
|
601
|
+
onClick: ()=>{
|
|
602
|
+
void save();
|
|
603
|
+
},
|
|
604
|
+
children: isSaving ? 'Zapisywanie…' : 'Zapisz w Bitsnap'
|
|
605
|
+
}),
|
|
606
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
607
|
+
buttonStyle: "secondary",
|
|
608
|
+
disabled: isLoading || isSaving,
|
|
609
|
+
onClick: ()=>{
|
|
610
|
+
void load();
|
|
611
|
+
},
|
|
612
|
+
children: isLoading && !isInitialLoad ? 'Odświeżanie…' : 'Odśwież'
|
|
613
|
+
}),
|
|
614
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
615
|
+
buttonStyle: "subtle",
|
|
616
|
+
className: "bitsnap-manager__open",
|
|
617
|
+
el: "anchor",
|
|
618
|
+
newTab: true,
|
|
619
|
+
url: `${dashboardUrl}/app/product/${encodeURIComponent(linkedProductID)}`,
|
|
620
|
+
children: "Otwórz w Bitsnap"
|
|
621
|
+
})
|
|
622
|
+
]
|
|
623
|
+
}),
|
|
624
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
625
|
+
className: "bitsnap-manager__meta",
|
|
626
|
+
children: [
|
|
627
|
+
/*#__PURE__*/ _jsx("span", {
|
|
628
|
+
children: "Produkt w Bitsnap"
|
|
629
|
+
}),
|
|
630
|
+
/*#__PURE__*/ _jsx("code", {
|
|
631
|
+
children: linkedProductID
|
|
632
|
+
}),
|
|
633
|
+
/*#__PURE__*/ _jsx(CopyToClipboard, {
|
|
634
|
+
value: linkedProductID
|
|
635
|
+
})
|
|
636
|
+
]
|
|
637
|
+
})
|
|
638
|
+
]
|
|
639
|
+
});
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
//# sourceMappingURL=BitsnapManager.js.map
|