bitsnap-payloadcms 0.0.0-stage → 0.1.1

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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +270 -2
  3. package/dist/client.d.ts +3 -0
  4. package/dist/client.js +5 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/constants.d.ts +7 -0
  7. package/dist/constants.js +8 -0
  8. package/dist/constants.js.map +1 -0
  9. package/dist/fields/getFields.d.ts +54 -0
  10. package/dist/fields/getFields.js +150 -0
  11. package/dist/fields/getFields.js.map +1 -0
  12. package/dist/fields/index.d.ts +2 -0
  13. package/dist/fields/index.js +3 -0
  14. package/dist/fields/index.js.map +1 -0
  15. package/dist/index.d.ts +7 -0
  16. package/dist/index.js +6 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/plugin.d.ts +15 -0
  19. package/dist/plugin.js +60 -0
  20. package/dist/plugin.js.map +1 -0
  21. package/dist/server/client.d.ts +47 -0
  22. package/dist/server/client.js +68 -0
  23. package/dist/server/client.js.map +1 -0
  24. package/dist/server/endpoints.d.ts +9 -0
  25. package/dist/server/endpoints.js +190 -0
  26. package/dist/server/endpoints.js.map +1 -0
  27. package/dist/server/errors.d.ts +9 -0
  28. package/dist/server/errors.js +35 -0
  29. package/dist/server/errors.js.map +1 -0
  30. package/dist/server/index.d.ts +6 -0
  31. package/dist/server/index.js +6 -0
  32. package/dist/server/index.js.map +1 -0
  33. package/dist/server/schemas.d.ts +55 -0
  34. package/dist/server/schemas.js +46 -0
  35. package/dist/server/schemas.js.map +1 -0
  36. package/dist/types.d.ts +75 -0
  37. package/dist/types.js +3 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/ui/BitsnapManager.css +159 -0
  40. package/dist/ui/BitsnapManager.d.ts +5 -0
  41. package/dist/ui/BitsnapManager.js +642 -0
  42. package/dist/ui/BitsnapManager.js.map +1 -0
  43. package/dist/ui/LinkToDoc.d.ts +4 -0
  44. package/dist/ui/LinkToDoc.js +48 -0
  45. package/dist/ui/LinkToDoc.js.map +1 -0
  46. package/dist/ui/ProductSelect.css +199 -0
  47. package/dist/ui/ProductSelect.d.ts +5 -0
  48. package/dist/ui/ProductSelect.js +146 -0
  49. package/dist/ui/ProductSelect.js.map +1 -0
  50. package/dist/ui/getBitsnapClientConfig.d.ts +13 -0
  51. package/dist/ui/getBitsnapClientConfig.js +30 -0
  52. package/dist/ui/getBitsnapClientConfig.js.map +1 -0
  53. package/dist/utils/formatCurrency.d.ts +6 -0
  54. package/dist/utils/formatCurrency.js +24 -0
  55. package/dist/utils/formatCurrency.js.map +1 -0
  56. 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