@fayz-ai/plugin-shop 0.24.4 → 0.24.6
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/dist/CheckoutSettings-GQRYZF6I.js +128 -0
- package/dist/CheckoutSettings-GQRYZF6I.js.map +1 -0
- package/dist/ContentPage-2PHOZSFZ.js +510 -0
- package/dist/ContentPage-2PHOZSFZ.js.map +1 -0
- package/dist/DeliveryStoreSettings-L2RXOVB6.js +172 -0
- package/dist/DeliveryStoreSettings-L2RXOVB6.js.map +1 -0
- package/dist/FulfillmentSettings-PW233UOK.js +61 -0
- package/dist/FulfillmentSettings-PW233UOK.js.map +1 -0
- package/dist/MarketsSettings-YUEVDLIR.js +19 -0
- package/dist/MarketsSettings-YUEVDLIR.js.map +1 -0
- package/dist/PaymentsSettings-5CI74TY2.js +318 -0
- package/dist/PaymentsSettings-5CI74TY2.js.map +1 -0
- package/dist/ProductContentTab-PSNDS7JZ.js +4 -0
- package/dist/ProductContentTab-PSNDS7JZ.js.map +1 -0
- package/dist/ProductListingTab-55Y5RXU7.js +4 -0
- package/dist/ProductListingTab-55Y5RXU7.js.map +1 -0
- package/dist/ProductMarketPricesTab-QD2ZXEWL.js +4 -0
- package/dist/ProductMarketPricesTab-QD2ZXEWL.js.map +1 -0
- package/dist/ShippingSettings-UBTNRBJ4.js +9 -0
- package/dist/ShippingSettings-UBTNRBJ4.js.map +1 -0
- package/dist/{ShopPage-NUIIWBCB.js → ShopPage-NARKHXOO.js} +6 -3
- package/dist/ShopPage-NARKHXOO.js.map +1 -0
- package/dist/StoreGeneralSettings-DEZ2CMVG.js +97 -0
- package/dist/StoreGeneralSettings-DEZ2CMVG.js.map +1 -0
- package/dist/chunk-2MXOKJQO.js +23 -0
- package/dist/chunk-2MXOKJQO.js.map +1 -0
- package/dist/chunk-4JM32Z7J.js +51 -0
- package/dist/chunk-4JM32Z7J.js.map +1 -0
- package/dist/chunk-53Y6VLAW.js +132 -0
- package/dist/chunk-53Y6VLAW.js.map +1 -0
- package/dist/chunk-6V2VAUWP.js +24 -0
- package/dist/chunk-6V2VAUWP.js.map +1 -0
- package/dist/chunk-FQQMEM26.js +59 -0
- package/dist/chunk-FQQMEM26.js.map +1 -0
- package/dist/chunk-JYT6BTRE.js +298 -0
- package/dist/chunk-JYT6BTRE.js.map +1 -0
- package/dist/chunk-MFEOIJI7.js +127 -0
- package/dist/chunk-MFEOIJI7.js.map +1 -0
- package/dist/chunk-R5CUDFOI.js +61 -0
- package/dist/chunk-R5CUDFOI.js.map +1 -0
- package/dist/chunk-STHGZWBQ.js +1455 -0
- package/dist/chunk-STHGZWBQ.js.map +1 -0
- package/dist/{chunk-XV3KZRQX.js → chunk-STZYH32S.js} +9 -140
- package/dist/chunk-STZYH32S.js.map +1 -0
- package/dist/chunk-TDV3SFXT.js +75 -0
- package/dist/chunk-TDV3SFXT.js.map +1 -0
- package/dist/chunk-YUG6XQHJ.js +55 -0
- package/dist/chunk-YUG6XQHJ.js.map +1 -0
- package/dist/data/registry-routes.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +129 -3456
- package/dist/index.js.map +1 -1
- package/dist/settings/ShippingSettings.d.ts.map +1 -1
- package/dist/views/EmailTemplatePage.d.ts.map +1 -1
- package/dist/views/EmailsPage.d.ts.map +1 -1
- package/dist/views/ProductContentTab.d.ts.map +1 -1
- package/dist/views/ProductMarketPricesTab.d.ts.map +1 -1
- package/dist/views/media-links.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/ShopPage-NUIIWBCB.js.map +0 -1
- package/dist/chunk-XV3KZRQX.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
1
|
+
import { shippingRegistryPath } from './chunk-6V2VAUWP.js';
|
|
2
|
+
import { ProductImagesTab } from './chunk-JYT6BTRE.js';
|
|
3
|
+
export { ProductContentTab } from './chunk-JYT6BTRE.js';
|
|
4
|
+
export { ProductMarketPricesTab } from './chunk-MFEOIJI7.js';
|
|
5
|
+
export { ProductListingTab } from './chunk-53Y6VLAW.js';
|
|
6
|
+
import { shopCustomerLookup, resolveTerm, candidateMeta } from './chunk-TDV3SFXT.js';
|
|
7
|
+
import { PACKAGES_REGISTRY, SHIPPING_ZONES_REGISTRY, SHIPPING_PROFILES_REGISTRY, packageMeasureLabel, packagesRegistry } from './chunk-STZYH32S.js';
|
|
8
|
+
import { resolve, invalidateProductLists, listProductsDetailed, applyQuery, listProductsCached } from './chunk-YUG6XQHJ.js';
|
|
9
|
+
import { errorMessage } from './chunk-2MXOKJQO.js';
|
|
10
|
+
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
11
|
+
import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, tl, lazyComponent, getActiveTenantId, getSupabaseClientOptional, registerMessageDefaults, registerComponent, formatCurrency, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, countByTenant, describeError, errorMessage as errorMessage$1, emitDataChanged, resolveDataProvider } from '@fayz-ai/core';
|
|
12
|
+
import { getShopTenantId, setShopTenantResolver, buildShopMessageDefaults, registerShippingLabelProvider, getShopProvider, buildShopDomainEvents, axesFromOptions, planVariantMatrix, validateVariantAxes, hasOnlyDefaultVariant, MAX_VARIANT_AXES, leavesNothingSellable, formatVariantCombination, applyVariantMatrix } from '@fayz-ai/shop';
|
|
13
|
+
import { fayzConnectorOAuth, describeConnectorAccount, useOrganizationStore, registerEntityPath, registerPersonLookup, createCrudPage, registerCrudNavTabs, adminNavigateTo, DetailTabHeader, useAgentSurface, askAI, CrudFormPage } from '@fayz-ai/admin';
|
|
14
|
+
import { Package, Eye, FileEdit, PackageX, Users, UsersRound, Filter, UserX, Tag, TicketCheck, Layers, CreditCard, CircleDollarSign, Plus, Loader2, WandSparkles, AlertTriangle, X, Trash2, ExternalLink, Box, ChevronDown } from 'lucide-react';
|
|
15
|
+
import { Button, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, Input, ModalFooter, Thumbnail, Checkbox, toast, ModalBody } from '@fayz-ai/ui';
|
|
9
16
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
10
17
|
import { listStockReasonCodes, resolveStockStore, createSupabaseStockStore } from '@fayz-ai/shop/inventory';
|
|
11
18
|
import { normalizeMovementType } from '@fayz-ai/inventory/domain/movement';
|
|
@@ -194,267 +201,38 @@ function buildShopOnboarding(siteRoute = "/site", withProducts = true) {
|
|
|
194
201
|
|
|
195
202
|
// src/integrations/melhor-envio/labelProvider.ts
|
|
196
203
|
var MELHOR_ENVIO_CARRIER = "melhor-envio";
|
|
197
|
-
function createMelhorEnvioLabelProvider(
|
|
204
|
+
function createMelhorEnvioLabelProvider(resolve2) {
|
|
198
205
|
const unsupported = () => {
|
|
199
206
|
throw new Error("Este painel n\xE3o sabe comprar etiquetas nesta loja.");
|
|
200
207
|
};
|
|
201
208
|
return {
|
|
202
209
|
id: MELHOR_ENVIO_CARRIER,
|
|
203
210
|
label: "Melhor Envio",
|
|
204
|
-
quote: async (orderId) => await
|
|
205
|
-
buy: async (orderId, serviceId) => await
|
|
206
|
-
print: async (orderId) => await
|
|
207
|
-
track: async (orderId) => await
|
|
211
|
+
quote: async (orderId) => await resolve2().quoteShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? unsupported(),
|
|
212
|
+
buy: async (orderId, serviceId) => await resolve2().buyShippingLabel?.(orderId, serviceId, MELHOR_ENVIO_CARRIER) ?? unsupported(),
|
|
213
|
+
print: async (orderId) => await resolve2().printShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? null,
|
|
214
|
+
track: async (orderId) => await resolve2().trackShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? null
|
|
208
215
|
};
|
|
209
216
|
}
|
|
210
|
-
function ProductImagesTab({ item, getProvider, footer }) {
|
|
211
|
-
const product = item;
|
|
212
|
-
const [images, setImages] = useState(null);
|
|
213
|
-
const [dragging, setDragging] = useState(false);
|
|
214
|
-
const [busy, setBusy] = useState(false);
|
|
215
|
-
const [error, setError] = useState(null);
|
|
216
|
-
const fileRef = useRef(null);
|
|
217
|
-
async function reload() {
|
|
218
|
-
const fresh = await getProvider().getProduct(product.id);
|
|
219
|
-
setImages(sortImages(fresh?.images ?? []));
|
|
220
|
-
}
|
|
221
|
-
useEffect(() => {
|
|
222
|
-
setImages(null);
|
|
223
|
-
void reload().catch((err) => {
|
|
224
|
-
setImages([]);
|
|
225
|
-
setError(errorMessage(err));
|
|
226
|
-
});
|
|
227
|
-
}, [product.id]);
|
|
228
|
-
async function run(action) {
|
|
229
|
-
setBusy(true);
|
|
230
|
-
setError(null);
|
|
231
|
-
try {
|
|
232
|
-
await action();
|
|
233
|
-
await reload();
|
|
234
|
-
} catch (err) {
|
|
235
|
-
setError(errorMessage(err));
|
|
236
|
-
} finally {
|
|
237
|
-
setBusy(false);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
async function upload(picked) {
|
|
241
|
-
const files = Array.from(picked ?? []).filter((file) => file.type.startsWith("image/"));
|
|
242
|
-
if (!files.length) return;
|
|
243
|
-
await run(async () => {
|
|
244
|
-
for (const file of files) {
|
|
245
|
-
await getProvider().uploadProductImage(product.id, file);
|
|
246
|
-
}
|
|
247
|
-
if (fileRef.current) fileRef.current.value = "";
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
function move(index, delta) {
|
|
251
|
-
const target = index + delta;
|
|
252
|
-
if (!images || target < 0 || target >= images.length) return;
|
|
253
|
-
const a = images[index];
|
|
254
|
-
const b = images[target];
|
|
255
|
-
void run(async () => {
|
|
256
|
-
const update2 = getProvider().updateProductImage;
|
|
257
|
-
if (!update2) throw new Error("Este provider n\xE3o suporta reordenar imagens.");
|
|
258
|
-
await update2(a.id, { sortOrder: b.sortOrder ?? target });
|
|
259
|
-
await update2(b.id, { sortOrder: a.sortOrder ?? index });
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
const update = getProvider().updateProductImage;
|
|
263
|
-
return (
|
|
264
|
-
// The whole tab takes a drop: aiming at a small box with six files in hand
|
|
265
|
-
// is the part of uploading that goes wrong.
|
|
266
|
-
/* @__PURE__ */ jsxs(
|
|
267
|
-
"div",
|
|
268
|
-
{
|
|
269
|
-
className: "space-y-4",
|
|
270
|
-
onDragOver: (event) => {
|
|
271
|
-
if (event.dataTransfer.types.includes("Files")) {
|
|
272
|
-
event.preventDefault();
|
|
273
|
-
setDragging(true);
|
|
274
|
-
}
|
|
275
|
-
},
|
|
276
|
-
onDragLeave: (event) => {
|
|
277
|
-
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
278
|
-
},
|
|
279
|
-
onDrop: (event) => {
|
|
280
|
-
if (!event.dataTransfer.files.length) return;
|
|
281
|
-
event.preventDefault();
|
|
282
|
-
setDragging(false);
|
|
283
|
-
void upload(event.dataTransfer.files);
|
|
284
|
-
},
|
|
285
|
-
children: [
|
|
286
|
-
/* @__PURE__ */ jsx(
|
|
287
|
-
DetailTabHeader,
|
|
288
|
-
{
|
|
289
|
-
title: "Fotos do produto",
|
|
290
|
-
description: "A capa \xE9 a foto do produto em todo lugar: no cadastro, no estoque, no cat\xE1logo, no carrinho e no pedido. As demais viram o carrossel na p\xE1gina do produto.",
|
|
291
|
-
actions: (
|
|
292
|
-
// The same Button as every other tab. It used to be a <label> hand-
|
|
293
|
-
// painted to look like one, which drifted the moment the real button
|
|
294
|
-
// changed — different height, no icon, no disabled state.
|
|
295
|
-
/* @__PURE__ */ jsxs(Button, { disabled: busy, onClick: () => fileRef.current?.click(), children: [
|
|
296
|
-
/* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
|
|
297
|
-
busy ? "Enviando\u2026" : "Adicionar fotos"
|
|
298
|
-
] })
|
|
299
|
-
)
|
|
300
|
-
}
|
|
301
|
-
),
|
|
302
|
-
/* @__PURE__ */ jsx(
|
|
303
|
-
"input",
|
|
304
|
-
{
|
|
305
|
-
ref: fileRef,
|
|
306
|
-
type: "file",
|
|
307
|
-
accept: "image/*",
|
|
308
|
-
multiple: true,
|
|
309
|
-
disabled: busy,
|
|
310
|
-
className: "hidden",
|
|
311
|
-
onChange: (event) => void upload(event.target.files)
|
|
312
|
-
}
|
|
313
|
-
),
|
|
314
|
-
error && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800", children: error }),
|
|
315
|
-
images === null ? /* @__PURE__ */ jsx("ul", { className: "fz-cards gap-3", "aria-busy": "true", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsxs("li", { className: "overflow-hidden rounded-lg border", children: [
|
|
316
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "aspect-square w-full rounded-none" }),
|
|
317
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-2 py-2", children: [
|
|
318
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-14" }),
|
|
319
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-20" })
|
|
320
|
-
] })
|
|
321
|
-
] }, i)) }) : images.length === 0 || dragging ? /* @__PURE__ */ jsxs(
|
|
322
|
-
Button,
|
|
323
|
-
{
|
|
324
|
-
variant: "bare",
|
|
325
|
-
size: "bare",
|
|
326
|
-
type: "button",
|
|
327
|
-
disabled: busy,
|
|
328
|
-
onClick: () => fileRef.current?.click(),
|
|
329
|
-
className: `font-normal whitespace-normal flex w-full flex-col items-center gap-2 rounded-lg border border-dashed px-4 py-10 text-center text-sm transition-colors ${dragging ? "border-primary bg-primary/5 text-foreground" : "text-muted-foreground hover:bg-muted/40"}`,
|
|
330
|
-
children: [
|
|
331
|
-
/* @__PURE__ */ jsx(UploadCloud, { className: "h-6 w-6", "aria-hidden": true }),
|
|
332
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children: dragging ? "Solte para enviar" : "Arraste as fotos para c\xE1, ou clique para escolher" }),
|
|
333
|
-
!dragging && /* @__PURE__ */ jsx("span", { className: "max-w-md text-xs", children: "Produtos com mais de uma foto vendem melhor \u2014 mostre a embalagem, o produto pronto e uma refer\xEAncia de tamanho." })
|
|
334
|
-
]
|
|
335
|
-
}
|
|
336
|
-
) : /* @__PURE__ */ jsx("ul", { className: "fz-cards gap-3", children: images.map((image, index) => /* @__PURE__ */ jsxs("li", { className: "overflow-hidden rounded-lg border", children: [
|
|
337
|
-
/* @__PURE__ */ jsxs("div", { className: "relative aspect-square bg-muted", children: [
|
|
338
|
-
/* @__PURE__ */ jsx("img", { src: image.url, alt: image.altText ?? "", className: "h-full w-full object-cover" }),
|
|
339
|
-
image.isPrimary && /* @__PURE__ */ jsx("span", { className: "absolute left-2 top-2 rounded-full bg-primary px-2 py-0.5 text-[11px] font-bold text-primary-foreground", children: "Capa" })
|
|
340
|
-
] }),
|
|
341
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-x-1 gap-y-1.5 px-2 py-1.5", children: [
|
|
342
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
343
|
-
/* @__PURE__ */ jsx(
|
|
344
|
-
IconButton,
|
|
345
|
-
{
|
|
346
|
-
label: "Mover para tr\xE1s",
|
|
347
|
-
disabled: busy || index === 0,
|
|
348
|
-
onClick: () => move(index, -1),
|
|
349
|
-
children: "\u2190"
|
|
350
|
-
}
|
|
351
|
-
),
|
|
352
|
-
/* @__PURE__ */ jsx(
|
|
353
|
-
IconButton,
|
|
354
|
-
{
|
|
355
|
-
label: "Mover para frente",
|
|
356
|
-
disabled: busy || index === images.length - 1,
|
|
357
|
-
onClick: () => move(index, 1),
|
|
358
|
-
children: "\u2192"
|
|
359
|
-
}
|
|
360
|
-
)
|
|
361
|
-
] }),
|
|
362
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
363
|
-
!image.isPrimary && update && /* @__PURE__ */ jsx(
|
|
364
|
-
Button,
|
|
365
|
-
{
|
|
366
|
-
variant: "bare",
|
|
367
|
-
size: "bare",
|
|
368
|
-
type: "button",
|
|
369
|
-
disabled: busy,
|
|
370
|
-
onClick: () => void run(() => update(image.id, { isPrimary: true })),
|
|
371
|
-
className: "font-normal whitespace-nowrap rounded px-1.5 py-0.5 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50",
|
|
372
|
-
children: "Usar como capa"
|
|
373
|
-
}
|
|
374
|
-
),
|
|
375
|
-
/* @__PURE__ */ jsx(
|
|
376
|
-
AskAIButton,
|
|
377
|
-
{
|
|
378
|
-
size: "sm",
|
|
379
|
-
variant: "outline",
|
|
380
|
-
className: "h-6 px-2 text-xs",
|
|
381
|
-
label: "IA",
|
|
382
|
-
media: { url: image.url, name: image.altText ?? product.name },
|
|
383
|
-
draft: "Melhora essa imagem",
|
|
384
|
-
hint: "o que voc\xEA quer mudar nessa foto",
|
|
385
|
-
intro: {
|
|
386
|
-
persona: "Est\xFAdio de imagem",
|
|
387
|
-
pitch: `Vamos melhorar a foto de ${product.name}.`,
|
|
388
|
-
description: "Posso limpar o fundo, corrigir luz e cor, e gerar uma vers\xE3o em resolu\xE7\xE3o maior para o cat\xE1logo.",
|
|
389
|
-
suggestions: [
|
|
390
|
-
{ label: "Fundo branco", prompt: "Remove o fundo e deixa branco" },
|
|
391
|
-
{ label: "Mais n\xEDtida", prompt: "Aumenta a resolu\xE7\xE3o e a nitidez" }
|
|
392
|
-
]
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
),
|
|
396
|
-
/* @__PURE__ */ jsx(
|
|
397
|
-
Button,
|
|
398
|
-
{
|
|
399
|
-
variant: "bare",
|
|
400
|
-
size: "bare",
|
|
401
|
-
type: "button",
|
|
402
|
-
disabled: busy,
|
|
403
|
-
onClick: () => void run(() => getProvider().deleteProductImage(image.id)),
|
|
404
|
-
className: "font-normal rounded px-1.5 py-0.5 text-xs text-red-600 hover:bg-red-50 disabled:opacity-50",
|
|
405
|
-
children: "Excluir"
|
|
406
|
-
}
|
|
407
|
-
)
|
|
408
|
-
] })
|
|
409
|
-
] })
|
|
410
|
-
] }, image.id)) }),
|
|
411
|
-
footer
|
|
412
|
-
]
|
|
413
|
-
}
|
|
414
|
-
)
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
function IconButton({ label, disabled, onClick, children }) {
|
|
418
|
-
return /* @__PURE__ */ jsx(
|
|
419
|
-
Button,
|
|
420
|
-
{
|
|
421
|
-
variant: "bare",
|
|
422
|
-
size: "bare",
|
|
423
|
-
type: "button",
|
|
424
|
-
"aria-label": label,
|
|
425
|
-
title: label,
|
|
426
|
-
disabled,
|
|
427
|
-
onClick,
|
|
428
|
-
className: "font-normal disabled:pointer-events-auto rounded border px-1.5 py-0.5 text-xs text-muted-foreground transition hover:bg-muted hover:text-foreground disabled:opacity-40",
|
|
429
|
-
children
|
|
430
|
-
}
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
function sortImages(images) {
|
|
434
|
-
return [...images].sort((a, b) => {
|
|
435
|
-
if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;
|
|
436
|
-
return (a.sortOrder ?? 0) - (b.sortOrder ?? 0);
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
217
|
function ProductVariantsTab({ item, getProvider }) {
|
|
440
218
|
const product = item;
|
|
441
219
|
const shop = getProvider();
|
|
442
220
|
const supported = typeof shop.getProductVariants === "function" && typeof shop.saveProductOptions === "function" && typeof shop.createProductVariant === "function" && typeof shop.updateProductVariant === "function";
|
|
443
|
-
const [axes, setAxes] =
|
|
444
|
-
const [variants, setVariants] =
|
|
445
|
-
const [busy, setBusy] =
|
|
446
|
-
const [adding, setAdding] =
|
|
447
|
-
const [error, setError] =
|
|
448
|
-
const [selected, setSelected] =
|
|
449
|
-
const [dirty, setDirty] =
|
|
450
|
-
const reload =
|
|
221
|
+
const [axes, setAxes] = React.useState([]);
|
|
222
|
+
const [variants, setVariants] = React.useState([]);
|
|
223
|
+
const [busy, setBusy] = React.useState(false);
|
|
224
|
+
const [adding, setAdding] = React.useState(false);
|
|
225
|
+
const [error, setError] = React.useState(null);
|
|
226
|
+
const [selected, setSelected] = React.useState(/* @__PURE__ */ new Set());
|
|
227
|
+
const [dirty, setDirty] = React.useState(false);
|
|
228
|
+
const reload = React.useCallback(async () => {
|
|
451
229
|
const set = await getProvider().getProductVariants(product.id, { includeArchived: true });
|
|
452
230
|
setAxes(axesFromOptions(set.options));
|
|
453
231
|
setVariants(set.variants);
|
|
454
232
|
setSelected(/* @__PURE__ */ new Set());
|
|
455
233
|
setDirty(false);
|
|
456
234
|
}, [getProvider, product.id]);
|
|
457
|
-
|
|
235
|
+
React.useEffect(() => {
|
|
458
236
|
if (!supported) return;
|
|
459
237
|
void reload().catch((e) => setError(message(e)));
|
|
460
238
|
}, [reload, supported]);
|
|
@@ -470,7 +248,7 @@ function ProductVariantsTab({ item, getProvider }) {
|
|
|
470
248
|
setBusy(false);
|
|
471
249
|
}
|
|
472
250
|
}
|
|
473
|
-
const plan =
|
|
251
|
+
const plan = React.useMemo(() => planVariantMatrix(axes, variants), [axes, variants]);
|
|
474
252
|
const axisError = validateVariantAxes(axes);
|
|
475
253
|
const onlyDefault = hasOnlyDefaultVariant(variants);
|
|
476
254
|
const rows = plan.rows;
|
|
@@ -590,7 +368,7 @@ function ProductVariantsTab({ item, getProvider }) {
|
|
|
590
368
|
] });
|
|
591
369
|
}
|
|
592
370
|
function AxisEditor({ axis, index, count, disabled, onChange, onMove, onRemove }) {
|
|
593
|
-
const [
|
|
371
|
+
const [draft, setDraft] = React.useState("");
|
|
594
372
|
function commit(raw) {
|
|
595
373
|
const values = raw.split(",").map((v) => v.trim()).filter(Boolean);
|
|
596
374
|
if (values.length === 0) return;
|
|
@@ -613,8 +391,8 @@ function AxisEditor({ axis, index, count, disabled, onChange, onMove, onRemove }
|
|
|
613
391
|
}
|
|
614
392
|
) }),
|
|
615
393
|
/* @__PURE__ */ jsx("span", { className: "flex-1" }),
|
|
616
|
-
/* @__PURE__ */ jsx(
|
|
617
|
-
/* @__PURE__ */ jsx(
|
|
394
|
+
/* @__PURE__ */ jsx(IconButton, { label: "Mover para cima", disabled: disabled || index === 0, onClick: () => onMove(-1), children: "\u2191" }),
|
|
395
|
+
/* @__PURE__ */ jsx(IconButton, { label: "Mover para baixo", disabled: disabled || index === count - 1, onClick: () => onMove(1), children: "\u2193" }),
|
|
618
396
|
/* @__PURE__ */ jsx(
|
|
619
397
|
Button,
|
|
620
398
|
{
|
|
@@ -649,16 +427,16 @@ function AxisEditor({ axis, index, count, disabled, onChange, onMove, onRemove }
|
|
|
649
427
|
/* @__PURE__ */ jsx("div", { className: "min-w-28 flex-1", children: /* @__PURE__ */ jsx(
|
|
650
428
|
Input,
|
|
651
429
|
{
|
|
652
|
-
value:
|
|
430
|
+
value: draft,
|
|
653
431
|
disabled,
|
|
654
432
|
onChange: (e) => setDraft(e.target.value),
|
|
655
433
|
onKeyDown: (e) => {
|
|
656
434
|
if (e.key === "Enter") {
|
|
657
435
|
e.preventDefault();
|
|
658
|
-
commit(
|
|
436
|
+
commit(draft);
|
|
659
437
|
}
|
|
660
438
|
},
|
|
661
|
-
onBlur: () => commit(
|
|
439
|
+
onBlur: () => commit(draft),
|
|
662
440
|
placeholder: axis.values.length === 0 ? "P, M, G" : "adicionar",
|
|
663
441
|
"aria-label": `Valores da op\xE7\xE3o ${index + 1}`,
|
|
664
442
|
className: "h-7 border-dashed bg-transparent px-2 text-xs shadow-none"
|
|
@@ -873,9 +651,9 @@ function VariantRow({ row, axes, product, busy, checked, onToggle, onSave, onSto
|
|
|
873
651
|
] });
|
|
874
652
|
}
|
|
875
653
|
function BulkBar({ count, disabled, onApply, onClear }) {
|
|
876
|
-
const [price, setPrice] =
|
|
877
|
-
const [weight, setWeight] =
|
|
878
|
-
const [unit, setUnit] =
|
|
654
|
+
const [price, setPrice] = React.useState("");
|
|
655
|
+
const [weight, setWeight] = React.useState("");
|
|
656
|
+
const [unit, setUnit] = React.useState("");
|
|
879
657
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-lg border bg-muted/30 px-3 py-2 text-xs", children: [
|
|
880
658
|
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
881
659
|
count,
|
|
@@ -952,19 +730,19 @@ function parseNumber(raw) {
|
|
|
952
730
|
return Number.isFinite(n) && n >= 0 ? n : null;
|
|
953
731
|
}
|
|
954
732
|
function NumberCell({ value, disabled, placeholder, onCommit, width = "w-20", integer }) {
|
|
955
|
-
const [
|
|
956
|
-
|
|
733
|
+
const [draft, setDraft] = React.useState(value == null ? "" : String(value));
|
|
734
|
+
React.useEffect(() => {
|
|
957
735
|
setDraft(value == null ? "" : String(value));
|
|
958
736
|
}, [value]);
|
|
959
737
|
return /* @__PURE__ */ jsx("div", { className: width, children: /* @__PURE__ */ jsx(
|
|
960
738
|
Input,
|
|
961
739
|
{
|
|
962
|
-
value:
|
|
740
|
+
value: draft,
|
|
963
741
|
disabled,
|
|
964
742
|
placeholder,
|
|
965
743
|
onChange: (e) => setDraft(e.target.value),
|
|
966
744
|
onBlur: () => {
|
|
967
|
-
const parsed = parseNumber(
|
|
745
|
+
const parsed = parseNumber(draft);
|
|
968
746
|
onCommit(parsed == null ? null : integer ? Math.trunc(parsed) : parsed);
|
|
969
747
|
},
|
|
970
748
|
className: `${COMPACT_INPUT} tabular-nums`
|
|
@@ -972,23 +750,23 @@ function NumberCell({ value, disabled, placeholder, onCommit, width = "w-20", in
|
|
|
972
750
|
) });
|
|
973
751
|
}
|
|
974
752
|
function TextCell({ value, disabled, placeholder, onCommit }) {
|
|
975
|
-
const [
|
|
976
|
-
|
|
753
|
+
const [draft, setDraft] = React.useState(value);
|
|
754
|
+
React.useEffect(() => {
|
|
977
755
|
setDraft(value);
|
|
978
756
|
}, [value]);
|
|
979
757
|
return /* @__PURE__ */ jsx("div", { className: "w-28", children: /* @__PURE__ */ jsx(
|
|
980
758
|
Input,
|
|
981
759
|
{
|
|
982
|
-
value:
|
|
760
|
+
value: draft,
|
|
983
761
|
disabled,
|
|
984
762
|
placeholder,
|
|
985
763
|
onChange: (e) => setDraft(e.target.value),
|
|
986
|
-
onBlur: () => onCommit(
|
|
764
|
+
onBlur: () => onCommit(draft.trim()),
|
|
987
765
|
className: COMPACT_INPUT
|
|
988
766
|
}
|
|
989
767
|
) });
|
|
990
768
|
}
|
|
991
|
-
function
|
|
769
|
+
function IconButton({ label, disabled, onClick, children }) {
|
|
992
770
|
return /* @__PURE__ */ jsx(
|
|
993
771
|
Button,
|
|
994
772
|
{
|
|
@@ -1023,9 +801,9 @@ function message(error) {
|
|
|
1023
801
|
return errorMessage$1(error);
|
|
1024
802
|
}
|
|
1025
803
|
function NewAxisModal({ open, onClose, onCreate }) {
|
|
1026
|
-
const [name, setName] =
|
|
1027
|
-
const [values, setValues] =
|
|
1028
|
-
|
|
804
|
+
const [name, setName] = React.useState("");
|
|
805
|
+
const [values, setValues] = React.useState([""]);
|
|
806
|
+
React.useEffect(() => {
|
|
1029
807
|
if (open) {
|
|
1030
808
|
setName("");
|
|
1031
809
|
setValues([""]);
|
|
@@ -1403,7 +1181,7 @@ function ProductPositionsTab({ item, getProvider }) {
|
|
|
1403
1181
|
const [noteOpen, setNoteOpen] = useState(false);
|
|
1404
1182
|
const [occurredOn, setOccurredOn] = useState(() => todayISO());
|
|
1405
1183
|
const [saving, setSaving] = useState(false);
|
|
1406
|
-
const quantityRef =
|
|
1184
|
+
const quantityRef = React.useRef(null);
|
|
1407
1185
|
const load = useCallback(async () => {
|
|
1408
1186
|
setLoading(true);
|
|
1409
1187
|
try {
|
|
@@ -1482,7 +1260,7 @@ function ProductPositionsTab({ item, getProvider }) {
|
|
|
1482
1260
|
const amount = Math.abs(Number(quantity) || 0);
|
|
1483
1261
|
const outgoing = type === "saida" || type === "perda";
|
|
1484
1262
|
const nextBalance = amount > 0 ? outgoing ? total.available - amount : total.available + amount : null;
|
|
1485
|
-
const live =
|
|
1263
|
+
const live = React.useRef({ product, variantId, total, getProvider, load });
|
|
1486
1264
|
live.current = { product, variantId, total, getProvider, load };
|
|
1487
1265
|
const contribution = useMemo(() => ({
|
|
1488
1266
|
id: "shop.product.positions",
|
|
@@ -1745,41 +1523,22 @@ function ProductPositionsTab({ item, getProvider }) {
|
|
|
1745
1523
|
] })
|
|
1746
1524
|
] });
|
|
1747
1525
|
}
|
|
1748
|
-
|
|
1749
|
-
// src/data/registry-routes.ts
|
|
1750
|
-
var SHIPPING_SETTINGS_TAB = "shop-shipping";
|
|
1751
|
-
var SHIPPING_SETTINGS_ROUTE = `/settings/${SHIPPING_SETTINGS_TAB}`;
|
|
1752
|
-
var PROPERTIES = "_properties";
|
|
1753
|
-
function shippingRegistryPath(registryId) {
|
|
1754
|
-
return `${SHIPPING_SETTINGS_ROUTE}/${PROPERTIES}/${registryId}`;
|
|
1755
|
-
}
|
|
1756
|
-
function shippingRecordPath(registryId, recordId, mode = "edit") {
|
|
1757
|
-
const base = `${shippingRegistryPath(registryId)}/${recordId}`;
|
|
1758
|
-
return mode === "edit" ? `${base}/edit` : base;
|
|
1759
|
-
}
|
|
1760
|
-
function shippingRegistryIdFromHash(hash) {
|
|
1761
|
-
const raw = hash ?? (typeof window === "undefined" ? "" : window.location.hash);
|
|
1762
|
-
const path = raw.startsWith("#") ? raw.slice(1) : raw;
|
|
1763
|
-
const marker = `${SHIPPING_SETTINGS_ROUTE}/${PROPERTIES}/`;
|
|
1764
|
-
if (!path.startsWith(marker)) return null;
|
|
1765
|
-
return path.slice(marker.length).split("/")[0] || null;
|
|
1766
|
-
}
|
|
1767
1526
|
var PACKAGES_ROUTE = shippingRegistryPath(PACKAGES_REGISTRY);
|
|
1768
1527
|
function PackageSelect({ value, onChange, getProvider }) {
|
|
1769
|
-
const shop =
|
|
1770
|
-
const [rows, setRows] =
|
|
1771
|
-
const [open, setOpen] =
|
|
1772
|
-
const [creating, setCreating] =
|
|
1773
|
-
const ref =
|
|
1774
|
-
const load =
|
|
1528
|
+
const shop = React.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
|
|
1529
|
+
const [rows, setRows] = React.useState(null);
|
|
1530
|
+
const [open, setOpen] = React.useState(false);
|
|
1531
|
+
const [creating, setCreating] = React.useState(false);
|
|
1532
|
+
const ref = React.useRef(null);
|
|
1533
|
+
const load = React.useCallback(() => {
|
|
1775
1534
|
if (typeof shop.listShippingPackages !== "function") {
|
|
1776
1535
|
setRows([]);
|
|
1777
1536
|
return;
|
|
1778
1537
|
}
|
|
1779
1538
|
void shop.listShippingPackages().then(setRows).catch(() => setRows([]));
|
|
1780
1539
|
}, [shop]);
|
|
1781
|
-
|
|
1782
|
-
|
|
1540
|
+
React.useEffect(load, [load]);
|
|
1541
|
+
React.useEffect(() => {
|
|
1783
1542
|
if (!open) return;
|
|
1784
1543
|
const away = (event) => {
|
|
1785
1544
|
if (ref.current && !ref.current.contains(event.target)) setOpen(false);
|
|
@@ -1880,8 +1639,8 @@ function PackageSelect({ value, onChange, getProvider }) {
|
|
|
1880
1639
|
] });
|
|
1881
1640
|
}
|
|
1882
1641
|
function NewPackageModal({ onClose, onCreated }) {
|
|
1883
|
-
const [error, setError] =
|
|
1884
|
-
const provider =
|
|
1642
|
+
const [error, setError] = React.useState(null);
|
|
1643
|
+
const provider = React.useMemo(
|
|
1885
1644
|
() => resolveDataProvider(packagesRegistry.entity),
|
|
1886
1645
|
[]
|
|
1887
1646
|
);
|
|
@@ -1930,9 +1689,9 @@ function Option({ label, hint, active, onClick }) {
|
|
|
1930
1689
|
}
|
|
1931
1690
|
var GENERAL = "__general__";
|
|
1932
1691
|
function ShippingProfileSelect({ value, onChange, getProvider }) {
|
|
1933
|
-
const shop =
|
|
1934
|
-
const [rows, setRows] =
|
|
1935
|
-
|
|
1692
|
+
const shop = React.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
|
|
1693
|
+
const [rows, setRows] = React.useState(null);
|
|
1694
|
+
React.useEffect(() => {
|
|
1936
1695
|
let cancelled = false;
|
|
1937
1696
|
if (typeof shop.listShippingProfiles !== "function") {
|
|
1938
1697
|
setRows([]);
|
|
@@ -2741,7 +2500,7 @@ var STATUS_LABEL2 = {
|
|
|
2741
2500
|
disabled: "Desativado"
|
|
2742
2501
|
};
|
|
2743
2502
|
function buildShopGiftCardEntity(currencyCode = "BRL") {
|
|
2744
|
-
const
|
|
2503
|
+
const money2 = (v) => formatCurrency(Number(v) || 0, currencyCode);
|
|
2745
2504
|
const fields = [
|
|
2746
2505
|
{
|
|
2747
2506
|
key: "code",
|
|
@@ -2769,7 +2528,7 @@ function buildShopGiftCardEntity(currencyCode = "BRL") {
|
|
|
2769
2528
|
hint: "Valor de face do cart\xE3o. N\xE3o muda depois de emitido.",
|
|
2770
2529
|
showInTable: true,
|
|
2771
2530
|
sortable: true,
|
|
2772
|
-
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right text-muted-foreground", children:
|
|
2531
|
+
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right text-muted-foreground", children: money2(v) })
|
|
2773
2532
|
},
|
|
2774
2533
|
{
|
|
2775
2534
|
key: "balance",
|
|
@@ -2783,7 +2542,7 @@ function buildShopGiftCardEntity(currencyCode = "BRL") {
|
|
|
2783
2542
|
hint: "O que ainda pode ser gasto. Cai a cada resgate.",
|
|
2784
2543
|
showInTable: true,
|
|
2785
2544
|
sortable: true,
|
|
2786
|
-
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right font-medium", children:
|
|
2545
|
+
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right font-medium", children: money2(v) })
|
|
2787
2546
|
},
|
|
2788
2547
|
{
|
|
2789
2548
|
key: "status",
|
|
@@ -2918,20 +2677,20 @@ function buildShopCustomerGroupEntity() {
|
|
|
2918
2677
|
fieldGroups: [{ id: "basic", label: "Grupo", columns: 2 }]
|
|
2919
2678
|
};
|
|
2920
2679
|
}
|
|
2921
|
-
function describeSegment(s,
|
|
2680
|
+
function describeSegment(s, money2) {
|
|
2922
2681
|
const parts = [];
|
|
2923
2682
|
if (s.minOrders != null && s.maxOrders != null) parts.push(`${s.minOrders}\u2013${s.maxOrders} pedidos`);
|
|
2924
2683
|
else if (s.minOrders != null) parts.push(`${s.minOrders}+ pedidos`);
|
|
2925
2684
|
else if (s.maxOrders != null) parts.push(`at\xE9 ${s.maxOrders} pedidos`);
|
|
2926
|
-
if (s.minSpent != null && s.maxSpent != null) parts.push(`gastou ${
|
|
2927
|
-
else if (s.minSpent != null) parts.push(`gastou ${
|
|
2928
|
-
else if (s.maxSpent != null) parts.push(`gastou at\xE9 ${
|
|
2685
|
+
if (s.minSpent != null && s.maxSpent != null) parts.push(`gastou ${money2(s.minSpent)}\u2013${money2(s.maxSpent)}`);
|
|
2686
|
+
else if (s.minSpent != null) parts.push(`gastou ${money2(s.minSpent)}+`);
|
|
2687
|
+
else if (s.maxSpent != null) parts.push(`gastou at\xE9 ${money2(s.maxSpent)}`);
|
|
2929
2688
|
if (s.joinedAfter) parts.push(`entrou ap\xF3s ${new Date(s.joinedAfter).toLocaleDateString("pt-BR")}`);
|
|
2930
2689
|
if (s.joinedBefore) parts.push(`entrou antes de ${new Date(s.joinedBefore).toLocaleDateString("pt-BR")}`);
|
|
2931
2690
|
return parts.length ? parts.join(" \xB7 ") : "Todos os clientes";
|
|
2932
2691
|
}
|
|
2933
2692
|
function buildShopCustomerSegmentEntity(currencyCode = "BRL") {
|
|
2934
|
-
const
|
|
2693
|
+
const money2 = (v) => formatCurrency(v, currencyCode);
|
|
2935
2694
|
const fields = [
|
|
2936
2695
|
{
|
|
2937
2696
|
key: "name",
|
|
@@ -3008,7 +2767,7 @@ function buildShopCustomerSegmentEntity(currencyCode = "BRL") {
|
|
|
3008
2767
|
showInForm: false,
|
|
3009
2768
|
showInTable: true,
|
|
3010
2769
|
sortable: false,
|
|
3011
|
-
renderCell: (_v, row) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: describeSegment(row,
|
|
2770
|
+
renderCell: (_v, row) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: describeSegment(row, money2) })
|
|
3012
2771
|
},
|
|
3013
2772
|
{
|
|
3014
2773
|
key: "matchCount",
|
|
@@ -3294,2326 +3053,25 @@ function createShopCustomerSegmentDataProvider(provider) {
|
|
|
3294
3053
|
name: String(input.name ?? ""),
|
|
3295
3054
|
description: input.description ?? null,
|
|
3296
3055
|
minOrders: input.minOrders ?? null,
|
|
3297
|
-
maxOrders: input.maxOrders ?? null,
|
|
3298
|
-
minSpent: input.minSpent ?? null,
|
|
3299
|
-
maxSpent: input.maxSpent ?? null,
|
|
3300
|
-
joinedAfter: input.joinedAfter || null,
|
|
3301
|
-
joinedBefore: input.joinedBefore || null
|
|
3302
|
-
});
|
|
3303
|
-
return { ...created, matchCount: 0 };
|
|
3304
|
-
},
|
|
3305
|
-
async update(id, data) {
|
|
3306
|
-
const p = requireSegments(resolve(provider));
|
|
3307
|
-
const { matchCount: _count, ...patch } = data;
|
|
3308
|
-
const updated = await p.updateCustomerSegment(id, patch);
|
|
3309
|
-
return { ...updated, matchCount: 0 };
|
|
3310
|
-
},
|
|
3311
|
-
async remove(id) {
|
|
3312
|
-
const p = requireSegments(resolve(provider));
|
|
3313
|
-
await p.deleteCustomerSegment(id);
|
|
3314
|
-
}
|
|
3315
|
-
};
|
|
3316
|
-
}
|
|
3317
|
-
var DEBOUNCE_MS = 400;
|
|
3318
|
-
function useStoreSettings(provider, defaults = {}) {
|
|
3319
|
-
const shop = React14__default.useMemo(
|
|
3320
|
-
() => provider ? resolve(provider) : getShopProvider(),
|
|
3321
|
-
[provider]
|
|
3322
|
-
);
|
|
3323
|
-
const supported = typeof shop.getStoreSettings === "function" && typeof shop.updateStoreSettings === "function";
|
|
3324
|
-
const [stored, setStored] = React14__default.useState(null);
|
|
3325
|
-
const [loading, setLoading] = React14__default.useState(supported);
|
|
3326
|
-
const [saving, setSaving] = React14__default.useState(false);
|
|
3327
|
-
const [error, setError] = React14__default.useState(null);
|
|
3328
|
-
const [loadError, setLoadError] = React14__default.useState(null);
|
|
3329
|
-
React14__default.useEffect(() => {
|
|
3330
|
-
if (!supported) return;
|
|
3331
|
-
let cancelled = false;
|
|
3332
|
-
setLoading(true);
|
|
3333
|
-
setLoadError(null);
|
|
3334
|
-
void shop.getStoreSettings().then((settings) => {
|
|
3335
|
-
if (!cancelled) setStored(settings);
|
|
3336
|
-
}).catch((e) => {
|
|
3337
|
-
if (!cancelled) setLoadError(errorMessage(e));
|
|
3338
|
-
}).finally(() => {
|
|
3339
|
-
if (!cancelled) setLoading(false);
|
|
3340
|
-
});
|
|
3341
|
-
return () => {
|
|
3342
|
-
cancelled = true;
|
|
3343
|
-
};
|
|
3344
|
-
}, [shop, supported]);
|
|
3345
|
-
const pendingRef = React14__default.useRef({});
|
|
3346
|
-
const timerRef = React14__default.useRef(null);
|
|
3347
|
-
React14__default.useEffect(() => () => {
|
|
3348
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3349
|
-
}, []);
|
|
3350
|
-
const set = React14__default.useCallback(
|
|
3351
|
-
(patch) => {
|
|
3352
|
-
setStored((current) => ({ ...current ?? {}, ...patch }));
|
|
3353
|
-
if (!supported) return;
|
|
3354
|
-
setError(null);
|
|
3355
|
-
setSaving(true);
|
|
3356
|
-
pendingRef.current = { ...pendingRef.current, ...patch };
|
|
3357
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3358
|
-
timerRef.current = setTimeout(() => {
|
|
3359
|
-
const payload = pendingRef.current;
|
|
3360
|
-
pendingRef.current = {};
|
|
3361
|
-
void shop.updateStoreSettings(payload).then((saved) => setStored(saved)).catch((e) => setError(errorMessage(e))).finally(() => setSaving(false));
|
|
3362
|
-
}, DEBOUNCE_MS);
|
|
3363
|
-
},
|
|
3364
|
-
[shop, supported]
|
|
3365
|
-
);
|
|
3366
|
-
const value = React14__default.useMemo(() => ({ ...defaults, ...stored ?? {} }), [defaults, stored]);
|
|
3367
|
-
return { value, stored, set, loading, saving, error, loadError, supported };
|
|
3368
|
-
}
|
|
3369
|
-
function SettingsState({
|
|
3370
|
-
loading,
|
|
3371
|
-
saving,
|
|
3372
|
-
error,
|
|
3373
|
-
loadError,
|
|
3374
|
-
supported,
|
|
3375
|
-
children
|
|
3376
|
-
}) {
|
|
3377
|
-
if (!supported) {
|
|
3378
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-4 text-sm", children: [
|
|
3379
|
-
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 font-medium text-amber-700 dark:text-amber-500", children: [
|
|
3380
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4 shrink-0" }),
|
|
3381
|
-
"Este app n\xE3o guarda as configura\xE7\xF5es da loja"
|
|
3382
|
-
] }),
|
|
3383
|
-
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-muted-foreground", children: [
|
|
3384
|
-
"O provedor de loja montado aqui n\xE3o implementa ",
|
|
3385
|
-
/* @__PURE__ */ jsx("code", { children: "getStoreSettings" }),
|
|
3386
|
-
". As op\xE7\xF5es abaixo n\xE3o seriam salvas, ent\xE3o n\xE3o s\xE3o mostradas."
|
|
3387
|
-
] })
|
|
3388
|
-
] });
|
|
3389
|
-
}
|
|
3390
|
-
if (loading) {
|
|
3391
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3392
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-28 w-full rounded-lg" }),
|
|
3393
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-40 w-full rounded-lg" })
|
|
3394
|
-
] });
|
|
3395
|
-
}
|
|
3396
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3397
|
-
loadError && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm", children: [
|
|
3398
|
-
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 font-medium text-destructive", children: [
|
|
3399
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4 shrink-0" }),
|
|
3400
|
-
"N\xE3o foi poss\xEDvel carregar as configura\xE7\xF5es da loja"
|
|
3401
|
-
] }),
|
|
3402
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground", children: "O que aparece abaixo s\xE3o os valores padr\xE3o, n\xE3o as suas escolhas. Recarregue a p\xE1gina antes de alterar algo \u2014 salvar agora sobrescreveria configura\xE7\xF5es que n\xE3o conseguimos ler." }),
|
|
3403
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: loadError })
|
|
3404
|
-
] }),
|
|
3405
|
-
children,
|
|
3406
|
-
/* @__PURE__ */ jsx("div", { className: "flex min-h-5 items-center gap-1.5 px-1 text-xs text-muted-foreground", children: error ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-destructive", children: [
|
|
3407
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
3408
|
-
"N\xE3o foi poss\xEDvel salvar: ",
|
|
3409
|
-
error
|
|
3410
|
-
] }) : saving ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3411
|
-
/* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 shrink-0 animate-spin" }),
|
|
3412
|
-
"Salvando\u2026"
|
|
3413
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3414
|
-
/* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
3415
|
-
"As altera\xE7\xF5es s\xE3o salvas automaticamente."
|
|
3416
|
-
] }) })
|
|
3417
|
-
] });
|
|
3418
|
-
}
|
|
3419
|
-
var CURRENCIES = [
|
|
3420
|
-
{ value: "BRL", label: "Real (R$)" },
|
|
3421
|
-
{ value: "USD", label: "US Dollar ($)" },
|
|
3422
|
-
{ value: "EUR", label: "Euro (\u20AC)" },
|
|
3423
|
-
{ value: "GBP", label: "British Pound (\xA3)" }
|
|
3424
|
-
];
|
|
3425
|
-
var LOCALES = [
|
|
3426
|
-
{ value: "pt-BR", label: "Portugu\xEAs (Brasil)" },
|
|
3427
|
-
{ value: "en-US", label: "English (US)" },
|
|
3428
|
-
{ value: "es-ES", label: "Espa\xF1ol" }
|
|
3429
|
-
];
|
|
3430
|
-
function StoreGeneralSettings({ currency, provider }) {
|
|
3431
|
-
const settings = useStoreSettings(provider, {
|
|
3432
|
-
currency: currency?.code ?? "BRL",
|
|
3433
|
-
locale: currency?.locale ?? "pt-BR",
|
|
3434
|
-
showOutOfStock: true,
|
|
3435
|
-
requireSku: false,
|
|
3436
|
-
notifyNewOrder: true,
|
|
3437
|
-
notifyLowStock: false
|
|
3438
|
-
});
|
|
3439
|
-
const { value, set } = settings;
|
|
3440
|
-
return /* @__PURE__ */ jsxs(SettingsState, { ...settings, children: [
|
|
3441
|
-
/* @__PURE__ */ jsxs(SettingsGroup, { title: "Loja", description: "Moeda e formata\xE7\xE3o da vitrine.", children: [
|
|
3442
|
-
/* @__PURE__ */ jsx(
|
|
3443
|
-
SelectRow,
|
|
3444
|
-
{
|
|
3445
|
-
label: "Moeda",
|
|
3446
|
-
description: "Moeda padr\xE3o para pre\xE7os e checkout.",
|
|
3447
|
-
value: value.currency ?? "BRL",
|
|
3448
|
-
options: CURRENCIES,
|
|
3449
|
-
onChange: (v) => set({ currency: v })
|
|
3450
|
-
}
|
|
3451
|
-
),
|
|
3452
|
-
/* @__PURE__ */ jsx(
|
|
3453
|
-
SelectRow,
|
|
3454
|
-
{
|
|
3455
|
-
label: "Idioma / formato",
|
|
3456
|
-
description: "Formata\xE7\xE3o de n\xFAmeros, pre\xE7os e datas.",
|
|
3457
|
-
value: value.locale ?? "pt-BR",
|
|
3458
|
-
options: LOCALES,
|
|
3459
|
-
onChange: (v) => set({ locale: v })
|
|
3460
|
-
}
|
|
3461
|
-
)
|
|
3462
|
-
] }),
|
|
3463
|
-
/* @__PURE__ */ jsxs(SettingsGroup, { title: "Cat\xE1logo", description: "Como os produtos aparecem e s\xE3o gerenciados.", children: [
|
|
3464
|
-
/* @__PURE__ */ jsx(
|
|
3465
|
-
ToggleRow,
|
|
3466
|
-
{
|
|
3467
|
-
label: "Mostrar produtos esgotados",
|
|
3468
|
-
description: "Exibir itens sem estoque na vitrine.",
|
|
3469
|
-
checked: value.showOutOfStock ?? true,
|
|
3470
|
-
onChange: (v) => set({ showOutOfStock: v })
|
|
3471
|
-
}
|
|
3472
|
-
),
|
|
3473
|
-
/* @__PURE__ */ jsx(
|
|
3474
|
-
ToggleRow,
|
|
3475
|
-
{
|
|
3476
|
-
label: "Exigir SKU",
|
|
3477
|
-
description: "Tornar o SKU obrigat\xF3rio ao criar produtos.",
|
|
3478
|
-
checked: value.requireSku ?? false,
|
|
3479
|
-
onChange: (v) => set({ requireSku: v })
|
|
3480
|
-
}
|
|
3481
|
-
)
|
|
3482
|
-
] }),
|
|
3483
|
-
/* @__PURE__ */ jsxs(SettingsGroup, { title: "Notifica\xE7\xF5es", description: "Avisos por e-mail da loja.", children: [
|
|
3484
|
-
/* @__PURE__ */ jsx(
|
|
3485
|
-
ToggleRow,
|
|
3486
|
-
{
|
|
3487
|
-
label: "E-mail de novo pedido",
|
|
3488
|
-
description: "Notificar a equipe a cada novo pedido.",
|
|
3489
|
-
checked: value.notifyNewOrder ?? true,
|
|
3490
|
-
onChange: (v) => set({ notifyNewOrder: v })
|
|
3491
|
-
}
|
|
3492
|
-
),
|
|
3493
|
-
/* @__PURE__ */ jsx(
|
|
3494
|
-
ToggleRow,
|
|
3495
|
-
{
|
|
3496
|
-
label: "Alerta de estoque baixo",
|
|
3497
|
-
description: "Avisar quando produtos atingirem o m\xEDnimo.",
|
|
3498
|
-
checked: value.notifyLowStock ?? false,
|
|
3499
|
-
onChange: (v) => set({ notifyLowStock: v })
|
|
3500
|
-
}
|
|
3501
|
-
)
|
|
3502
|
-
] })
|
|
3503
|
-
] });
|
|
3504
|
-
}
|
|
3505
|
-
|
|
3506
|
-
// src/settings/payment-provider.ts
|
|
3507
|
-
function resolvePaymentProvider(current, live) {
|
|
3508
|
-
if (live.length === 0) return null;
|
|
3509
|
-
if (current === "") return null;
|
|
3510
|
-
if (current && live.includes(current)) return null;
|
|
3511
|
-
return live[0];
|
|
3512
|
-
}
|
|
3513
|
-
function PaymentProviderRow({ connector, selected, onSelect, onOpen }) {
|
|
3514
|
-
const live = connector.state === "connected";
|
|
3515
|
-
return /* @__PURE__ */ jsxs("li", { className: `flex items-start gap-1 transition-colors ${selected ? "bg-primary/5" : ""}`, children: [
|
|
3516
|
-
/* @__PURE__ */ jsxs(
|
|
3517
|
-
Button,
|
|
3518
|
-
{
|
|
3519
|
-
variant: "bare",
|
|
3520
|
-
size: "bare",
|
|
3521
|
-
type: "button",
|
|
3522
|
-
role: "radio",
|
|
3523
|
-
"aria-checked": selected,
|
|
3524
|
-
disabled: !live,
|
|
3525
|
-
onClick: () => onSelect(connector),
|
|
3526
|
-
className: `font-normal justify-start whitespace-normal flex min-w-0 flex-1 items-start gap-3 p-4 text-left ${live ? "hover:bg-muted/40" : "cursor-default disabled:opacity-70"}`,
|
|
3527
|
-
children: [
|
|
3528
|
-
/* @__PURE__ */ jsx(
|
|
3529
|
-
"span",
|
|
3530
|
-
{
|
|
3531
|
-
"aria-hidden": true,
|
|
3532
|
-
className: `mt-1.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border ${selected ? "border-primary" : "border-muted-foreground/40"} ${live ? "" : "opacity-30"}`,
|
|
3533
|
-
children: selected && /* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full bg-primary" })
|
|
3534
|
-
}
|
|
3535
|
-
),
|
|
3536
|
-
/* @__PURE__ */ jsx(ConnectorMark, { connector }),
|
|
3537
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
3538
|
-
/* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-2", children: [
|
|
3539
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: connector.name }),
|
|
3540
|
-
/* @__PURE__ */ jsx(StateChip, { state: connector.state })
|
|
3541
|
-
] }),
|
|
3542
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: selected ? "Cobra os pedidos desta loja." : live ? "Conectado. Clique para passar a cobran\xE7a para ele." : connector.description ?? "Ainda n\xE3o conectado." })
|
|
3543
|
-
] })
|
|
3544
|
-
]
|
|
3545
|
-
}
|
|
3546
|
-
),
|
|
3547
|
-
/* @__PURE__ */ jsx(
|
|
3548
|
-
Button,
|
|
3549
|
-
{
|
|
3550
|
-
variant: "bare",
|
|
3551
|
-
size: "bare",
|
|
3552
|
-
type: "button",
|
|
3553
|
-
onClick: () => onOpen(connector),
|
|
3554
|
-
className: "font-normal shrink-0 self-center whitespace-nowrap px-4 text-xs text-muted-foreground underline decoration-dotted underline-offset-4 hover:text-foreground",
|
|
3555
|
-
children: live ? "Configurar conex\xE3o" : "Conectar"
|
|
3556
|
-
}
|
|
3557
|
-
)
|
|
3558
|
-
] });
|
|
3559
|
-
}
|
|
3560
|
-
function ManualChargeRow({ selected, onSelect }) {
|
|
3561
|
-
return /* @__PURE__ */ jsx("li", { className: selected ? "bg-primary/5" : "", children: /* @__PURE__ */ jsxs(
|
|
3562
|
-
Button,
|
|
3563
|
-
{
|
|
3564
|
-
variant: "bare",
|
|
3565
|
-
size: "bare",
|
|
3566
|
-
type: "button",
|
|
3567
|
-
role: "radio",
|
|
3568
|
-
"aria-checked": selected,
|
|
3569
|
-
onClick: onSelect,
|
|
3570
|
-
className: "font-normal justify-start rounded-none whitespace-normal flex w-full items-start gap-3 p-4 text-left hover:bg-muted/40",
|
|
3571
|
-
children: [
|
|
3572
|
-
/* @__PURE__ */ jsx(
|
|
3573
|
-
"span",
|
|
3574
|
-
{
|
|
3575
|
-
"aria-hidden": true,
|
|
3576
|
-
className: `mt-1.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border ${selected ? "border-primary" : "border-muted-foreground/40"}`,
|
|
3577
|
-
children: selected && /* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full bg-primary" })
|
|
3578
|
-
}
|
|
3579
|
-
),
|
|
3580
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
3581
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Nenhum \u2014 eu confirmo os pagamentos \xE0 m\xE3o" }),
|
|
3582
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: "Pix por chave, dinheiro na entrega, combinado com a loja. O pedido nasce pendente e espera voc\xEA confirmar que o dinheiro caiu." })
|
|
3583
|
-
] })
|
|
3584
|
-
]
|
|
3585
|
-
}
|
|
3586
|
-
) });
|
|
3587
|
-
}
|
|
3588
|
-
var NONE = "__none";
|
|
3589
|
-
var CARD_KIND = {
|
|
3590
|
-
credit_card: "card_receivable_credit",
|
|
3591
|
-
debit_card: "card_receivable_debit"
|
|
3592
|
-
};
|
|
3593
|
-
var CARD_LABEL = {
|
|
3594
|
-
credit_card: "Cart\xE3o de cr\xE9dito cai em",
|
|
3595
|
-
debit_card: "Cart\xE3o de d\xE9bito cai em"
|
|
3596
|
-
};
|
|
3597
|
-
function useReceivingAccountOptions() {
|
|
3598
|
-
const [accounts, setAccounts] = React14__default.useState(null);
|
|
3599
|
-
React14__default.useEffect(() => {
|
|
3600
|
-
const db = getSupabaseClientOptional();
|
|
3601
|
-
if (!db) {
|
|
3602
|
-
setAccounts([]);
|
|
3603
|
-
return;
|
|
3604
|
-
}
|
|
3605
|
-
let cancelled = false;
|
|
3606
|
-
db.from("plg_financial_accounts").select("id, name, kind").eq("is_active", true).in("kind", ["bank", "cash_physical", "card_receivable_credit", "card_receivable_debit"]).order("name").then(({ data }) => {
|
|
3607
|
-
if (!cancelled) setAccounts(data ?? []);
|
|
3608
|
-
});
|
|
3609
|
-
return () => {
|
|
3610
|
-
cancelled = true;
|
|
3611
|
-
};
|
|
3612
|
-
}, []);
|
|
3613
|
-
return accounts;
|
|
3614
|
-
}
|
|
3615
|
-
function ReceivingAccountsGroup({ provider, providerName, methods, value, onChange }) {
|
|
3616
|
-
const accounts = useReceivingAccountOptions();
|
|
3617
|
-
const current = value?.[provider] ?? {};
|
|
3618
|
-
const set = (key, id) => {
|
|
3619
|
-
const nextForProvider = { ...current };
|
|
3620
|
-
if (id === NONE) delete nextForProvider[key];
|
|
3621
|
-
else nextForProvider[key] = id;
|
|
3622
|
-
onChange({ ...value ?? {}, [provider]: nextForProvider });
|
|
3623
|
-
};
|
|
3624
|
-
const optionsFor = (kinds) => [
|
|
3625
|
-
{ value: NONE, label: "N\xE3o definido" },
|
|
3626
|
-
...(accounts ?? []).filter((a) => kinds.includes(a.kind)).map((a) => ({ value: a.id, label: a.name }))
|
|
3627
|
-
];
|
|
3628
|
-
const cards = ["credit_card", "debit_card"].filter((m) => methods.length === 0 ? m === "credit_card" : methods.includes(m));
|
|
3629
|
-
return /* @__PURE__ */ jsxs(
|
|
3630
|
-
SettingsGroup,
|
|
3631
|
-
{
|
|
3632
|
-
title: "Onde o dinheiro cai",
|
|
3633
|
-
description: `A conta do Financeiro em que cada venda paga pelo ${providerName} \xE9 lan\xE7ada. Sem uma conta aqui, o lan\xE7amento vai para a \xFAnica conta banc\xE1ria da loja.`,
|
|
3634
|
-
children: [
|
|
3635
|
-
/* @__PURE__ */ jsx(
|
|
3636
|
-
SelectRow,
|
|
3637
|
-
{
|
|
3638
|
-
label: "Pix, boleto e saldo caem em",
|
|
3639
|
-
description: `Normalmente a conta ${providerName} cadastrada em Financeiro \u203A Contas.`,
|
|
3640
|
-
value: current.default ?? NONE,
|
|
3641
|
-
options: optionsFor(["bank", "cash_physical"]),
|
|
3642
|
-
onChange: (id) => set("default", id),
|
|
3643
|
-
disabled: accounts === null
|
|
3644
|
-
}
|
|
3645
|
-
),
|
|
3646
|
-
cards.map((method) => /* @__PURE__ */ jsx(
|
|
3647
|
-
SelectRow,
|
|
3648
|
-
{
|
|
3649
|
-
label: CARD_LABEL[method],
|
|
3650
|
-
description: "Uma conta de cart\xE3o a receber. Sem ela, a venda no cart\xE3o n\xE3o d\xE1 baixa.",
|
|
3651
|
-
value: current[method] ?? NONE,
|
|
3652
|
-
options: optionsFor([CARD_KIND[method]]),
|
|
3653
|
-
onChange: (id) => set(method, id),
|
|
3654
|
-
disabled: accounts === null
|
|
3655
|
-
},
|
|
3656
|
-
method
|
|
3657
|
-
))
|
|
3658
|
-
]
|
|
3659
|
-
}
|
|
3660
|
-
);
|
|
3661
|
-
}
|
|
3662
|
-
var METHODS = [
|
|
3663
|
-
{ value: "pix", label: "Pix", hint: "Chave ou QR Code" },
|
|
3664
|
-
{ value: "credit_card", label: "Cart\xE3o de cr\xE9dito", hint: "Gateway ou maquininha" },
|
|
3665
|
-
{ value: "debit_card", label: "Cart\xE3o de d\xE9bito", hint: "Gateway ou maquininha" },
|
|
3666
|
-
{ value: "boleto", label: "Boleto", hint: "Enviado por e-mail" },
|
|
3667
|
-
{ value: "cash", label: "Dinheiro", hint: "Na entrega ou no balc\xE3o" },
|
|
3668
|
-
{ value: "other", label: "Combinar com a loja", hint: "A loja acerta o pagamento" }
|
|
3669
|
-
];
|
|
3670
|
-
var PAYMENT_CATEGORIES = ["payments"];
|
|
3671
|
-
function PaymentsSettings({ provider }) {
|
|
3672
|
-
const settings = useStoreSettings(provider);
|
|
3673
|
-
const { value, set } = settings;
|
|
3674
|
-
const { connectors } = useConnectors({ hostPluginId: "shop", includeGrants: false });
|
|
3675
|
-
const [adding, setAdding] = React14__default.useState(false);
|
|
3676
|
-
const [selected, setSelected] = React14__default.useState(null);
|
|
3677
|
-
const panelAvailable = useConnectorsPanelAvailable();
|
|
3678
|
-
const openJourney = React14__default.useCallback(
|
|
3679
|
-
(connector) => {
|
|
3680
|
-
if (panelAvailable) openConnectorsPanel(connector);
|
|
3681
|
-
else setSelected(connector);
|
|
3682
|
-
},
|
|
3683
|
-
[panelAvailable]
|
|
3684
|
-
);
|
|
3685
|
-
const payment = React14__default.useMemo(
|
|
3686
|
-
() => (connectors ?? []).filter((c) => c.category && PAYMENT_CATEGORIES.includes(c.category)),
|
|
3687
|
-
[connectors]
|
|
3688
|
-
);
|
|
3689
|
-
const live = React14__default.useMemo(() => payment.filter((c) => c.state === "connected"), [payment]);
|
|
3690
|
-
const charging = live.find((c) => c.slug === value.paymentProvider);
|
|
3691
|
-
const chosen = value.paymentMethods ?? [];
|
|
3692
|
-
const staleProvider = !!value.paymentProvider && !live.some((c) => c.slug === value.paymentProvider) && live.length === 0;
|
|
3693
|
-
const movedFrom = React14__default.useRef(null);
|
|
3694
|
-
React14__default.useEffect(() => {
|
|
3695
|
-
if (!settings.supported || settings.loading) return;
|
|
3696
|
-
const next = resolvePaymentProvider(value.paymentProvider, live.map((c) => c.slug));
|
|
3697
|
-
if (!next) return;
|
|
3698
|
-
if (value.paymentProvider) movedFrom.current = value.paymentProvider;
|
|
3699
|
-
set({ paymentProvider: next });
|
|
3700
|
-
}, [live, value.paymentProvider, settings.supported, settings.loading, set]);
|
|
3701
|
-
return /* @__PURE__ */ jsxs(SettingsState, { ...settings, children: [
|
|
3702
|
-
/* @__PURE__ */ jsxs(
|
|
3703
|
-
SettingsGroup,
|
|
3704
|
-
{
|
|
3705
|
-
title: "Formas de pagamento",
|
|
3706
|
-
description: "O que o cliente pode escolher no checkout. Um pedido que chegar com outra forma \xE9 recusado pelo servidor.",
|
|
3707
|
-
children: [
|
|
3708
|
-
/* @__PURE__ */ jsx(
|
|
3709
|
-
MultiSelectRow,
|
|
3710
|
-
{
|
|
3711
|
-
label: "Aceito na loja",
|
|
3712
|
-
description: "Nada escolhido = a vitrine oferece o que o app declarou, como hoje.",
|
|
3713
|
-
value: chosen,
|
|
3714
|
-
options: METHODS.map((m) => ({ value: m.value, label: m.label })),
|
|
3715
|
-
onChange: (next) => set({ paymentMethods: next })
|
|
3716
|
-
}
|
|
3717
|
-
),
|
|
3718
|
-
chosen.length > 0 && !value.paymentProvider && /* @__PURE__ */ jsxs("p", { className: "flex items-start gap-2 py-3 text-xs text-muted-foreground", children: [
|
|
3719
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
|
|
3720
|
-
"Sem um gateway cobrando, todo pedido nasce ",
|
|
3721
|
-
/* @__PURE__ */ jsx("strong", { className: "font-medium", children: "pendente" }),
|
|
3722
|
-
" e espera voc\xEA confirmar que o dinheiro caiu. Isso funciona \u2014 \xE9 como as lojas com Pix por chave e pagamento na entrega operam \u2014 mas a confirma\xE7\xE3o \xE9 sua."
|
|
3723
|
-
] })
|
|
3724
|
-
]
|
|
3725
|
-
}
|
|
3726
|
-
),
|
|
3727
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
3728
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4 px-1 pb-1 pt-4", children: [
|
|
3729
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
3730
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Quem cobra" }),
|
|
3731
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "O provedor marcado \xE9 quem abre a cobran\xE7a quando o cliente finaliza a compra \u2014 s\xF3 um cobra por vez. A credencial vai para o cofre, nunca para o banco da loja." })
|
|
3732
|
-
] }),
|
|
3733
|
-
/* @__PURE__ */ jsx(AddConnectorButton, { onClick: () => setAdding(true) })
|
|
3734
|
-
] }),
|
|
3735
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card shadow-sm", children: payment.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-4 text-sm text-muted-foreground", children: "Nenhum provedor de pagamento dispon\xEDvel para este app ainda." }) : /* @__PURE__ */ jsxs("ul", { className: "divide-y", role: "radiogroup", "aria-label": "Quem cobra", children: [
|
|
3736
|
-
payment.map((connector) => /* @__PURE__ */ jsx(
|
|
3737
|
-
PaymentProviderRow,
|
|
3738
|
-
{
|
|
3739
|
-
connector,
|
|
3740
|
-
selected: value.paymentProvider === connector.slug,
|
|
3741
|
-
onSelect: (c) => {
|
|
3742
|
-
movedFrom.current = null;
|
|
3743
|
-
set({ paymentProvider: c.slug });
|
|
3744
|
-
},
|
|
3745
|
-
onOpen: openJourney
|
|
3746
|
-
},
|
|
3747
|
-
connector.id
|
|
3748
|
-
)),
|
|
3749
|
-
/* @__PURE__ */ jsx(
|
|
3750
|
-
ManualChargeRow,
|
|
3751
|
-
{
|
|
3752
|
-
selected: value.paymentProvider === "",
|
|
3753
|
-
onSelect: () => {
|
|
3754
|
-
movedFrom.current = null;
|
|
3755
|
-
set({ paymentProvider: "" });
|
|
3756
|
-
}
|
|
3757
|
-
}
|
|
3758
|
-
)
|
|
3759
|
-
] }) }),
|
|
3760
|
-
movedFrom.current && /* @__PURE__ */ jsxs("p", { className: "flex items-start gap-2 px-1 pt-2 text-xs text-muted-foreground", children: [
|
|
3761
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600" }),
|
|
3762
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
3763
|
-
/* @__PURE__ */ jsx("strong", { className: "font-medium", children: movedFrom.current }),
|
|
3764
|
-
" n\xE3o est\xE1 mais conectado, ent\xE3o a cobran\xE7a passou para o provedor marcado acima. Reconecte-o para voltar."
|
|
3765
|
-
] })
|
|
3766
|
-
] }),
|
|
3767
|
-
staleProvider && /* @__PURE__ */ jsxs("p", { className: "flex items-start gap-2 px-1 pt-2 text-xs text-destructive", children: [
|
|
3768
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
|
|
3769
|
-
"A loja cobra por ",
|
|
3770
|
-
/* @__PURE__ */ jsx("strong", { className: "font-medium", children: value.paymentProvider }),
|
|
3771
|
-
", que n\xE3o est\xE1 conectado agora, e n\xE3o h\xE1 outro para assumir. Enquanto isso os pedidos nascem pendentes."
|
|
3772
|
-
] })
|
|
3773
|
-
] }),
|
|
3774
|
-
charging && /* @__PURE__ */ jsx(
|
|
3775
|
-
ReceivingAccountsGroup,
|
|
3776
|
-
{
|
|
3777
|
-
provider: charging.slug,
|
|
3778
|
-
providerName: charging.name,
|
|
3779
|
-
methods: chosen,
|
|
3780
|
-
value: value.receivingAccounts,
|
|
3781
|
-
onChange: (receivingAccounts) => set({ receivingAccounts })
|
|
3782
|
-
}
|
|
3783
|
-
),
|
|
3784
|
-
/* @__PURE__ */ jsx(
|
|
3785
|
-
AddConnectorPanel,
|
|
3786
|
-
{
|
|
3787
|
-
open: adding,
|
|
3788
|
-
onOpenChange: setAdding,
|
|
3789
|
-
connectors,
|
|
3790
|
-
categories: PAYMENT_CATEGORIES,
|
|
3791
|
-
onSelect: (connector) => {
|
|
3792
|
-
setAdding(false);
|
|
3793
|
-
openJourney(connector);
|
|
3794
|
-
}
|
|
3795
|
-
}
|
|
3796
|
-
),
|
|
3797
|
-
/* @__PURE__ */ jsx(
|
|
3798
|
-
ConnectorDetail,
|
|
3799
|
-
{
|
|
3800
|
-
connector: selected,
|
|
3801
|
-
open: !!selected,
|
|
3802
|
-
onOpenChange: (open) => {
|
|
3803
|
-
if (!open) setSelected(null);
|
|
3804
|
-
}
|
|
3805
|
-
}
|
|
3806
|
-
)
|
|
3807
|
-
] });
|
|
3808
|
-
}
|
|
3809
|
-
var FIELDS = [
|
|
3810
|
-
{ key: "name", label: "Nome completo", locked: true, hint: "Sempre pedido \u2014 \xE9 para quem a entrega vai." },
|
|
3811
|
-
{ key: "email", label: "E-mail", locked: true, hint: "Sempre pedido \u2014 \xE9 por onde o pedido \xE9 confirmado." },
|
|
3812
|
-
{ key: "phone", label: "Telefone", hint: "Vai junto com o endere\xE7o \u2014 \xE9 por onde o entregador liga." },
|
|
3813
|
-
// Back, now that 0050 gives it a column. Deliberately NOT locked: a store that
|
|
3814
|
-
// neither charges through a gateway nor issues an invoice has no use for it.
|
|
3815
|
-
// The checkout escalates it to required on its own when a gateway is on —
|
|
3816
|
-
// that is a fact about the charge, not a preference the merchant can waive.
|
|
3817
|
-
{ key: "document", label: "CPF / CNPJ", hint: "Obrigat\xF3rio quando a loja cobra por gateway ou emite nota \u2014 nesse caso a vitrine j\xE1 pede sozinha." },
|
|
3818
|
-
{ key: "postalCode", label: "CEP", locked: true, hint: "Sempre pedido \u2014 \xE9 o que cota o frete." },
|
|
3819
|
-
{ key: "street", label: "Endere\xE7o", locked: true },
|
|
3820
|
-
{ key: "number", label: "N\xFAmero", locked: true, hint: "Sem n\xFAmero o entregador n\xE3o chega." },
|
|
3821
|
-
{ key: "complement", label: "Complemento" },
|
|
3822
|
-
{ key: "district", label: "Bairro", locked: true },
|
|
3823
|
-
{ key: "city", label: "Cidade", locked: true },
|
|
3824
|
-
{ key: "state", label: "UF", locked: true }
|
|
3825
|
-
];
|
|
3826
|
-
var REQUIREMENT = [
|
|
3827
|
-
{ value: "off", label: "N\xE3o pedir" },
|
|
3828
|
-
{ value: "optional", label: "Pedir (opcional)" },
|
|
3829
|
-
{ value: "required", label: "Pedir (obrigat\xF3rio)" }
|
|
3830
|
-
];
|
|
3831
|
-
function toRequirement(policy, locked) {
|
|
3832
|
-
if (locked) return "required";
|
|
3833
|
-
if (policy?.visible === false) return "off";
|
|
3834
|
-
return policy?.required ? "required" : "optional";
|
|
3835
|
-
}
|
|
3836
|
-
function fromRequirement(choice) {
|
|
3837
|
-
if (choice === "off") return { visible: false, required: false };
|
|
3838
|
-
return { visible: true, required: choice === "required" };
|
|
3839
|
-
}
|
|
3840
|
-
function CheckoutSettings({ provider }) {
|
|
3841
|
-
const settings = useStoreSettings(provider, {
|
|
3842
|
-
guestCheckout: true,
|
|
3843
|
-
requireConfirmedEmail: false,
|
|
3844
|
-
marketingOptIn: false,
|
|
3845
|
-
discountsEnabled: true
|
|
3846
|
-
});
|
|
3847
|
-
const { value, set } = settings;
|
|
3848
|
-
const fields = value.checkoutFields ?? {};
|
|
3849
|
-
const setField = (key, choice) => set({ checkoutFields: { ...fields, [key]: fromRequirement(choice) } });
|
|
3850
|
-
return /* @__PURE__ */ jsxs(SettingsState, { ...settings, children: [
|
|
3851
|
-
/* @__PURE__ */ jsxs(
|
|
3852
|
-
SettingsGroup,
|
|
3853
|
-
{
|
|
3854
|
-
title: "Contas de cliente",
|
|
3855
|
-
description: "Se o comprador precisa de uma conta antes de finalizar.",
|
|
3856
|
-
children: [
|
|
3857
|
-
/* @__PURE__ */ jsx(
|
|
3858
|
-
ToggleRow,
|
|
3859
|
-
{
|
|
3860
|
-
label: "Permitir compra sem cadastro",
|
|
3861
|
-
description: "Desligado, o comprador precisa entrar ou criar conta. Esta \xE9 imposta no servidor: um pedido sem conta \xE9 recusado.",
|
|
3862
|
-
checked: value.guestCheckout ?? true,
|
|
3863
|
-
onChange: (v) => set({ guestCheckout: v })
|
|
3864
|
-
}
|
|
3865
|
-
),
|
|
3866
|
-
/* @__PURE__ */ jsx(
|
|
3867
|
-
ToggleRow,
|
|
3868
|
-
{
|
|
3869
|
-
label: "Exigir e-mail confirmado",
|
|
3870
|
-
description: "O comprador recebe um link ao se cadastrar e s\xF3 finaliza depois de clicar. Vale na vitrine \u2014 confirmar e-mail \xE9 um fato da conta, n\xE3o do pedido.",
|
|
3871
|
-
checked: value.requireConfirmedEmail ?? false,
|
|
3872
|
-
onChange: (v) => set({ requireConfirmedEmail: v }),
|
|
3873
|
-
disabled: value.guestCheckout !== false
|
|
3874
|
-
}
|
|
3875
|
-
),
|
|
3876
|
-
value.guestCheckout !== false && /* @__PURE__ */ jsx("p", { className: "py-3 text-xs text-muted-foreground", children: "\xABExigir e-mail confirmado\xBB s\xF3 faz sentido com o cadastro obrigat\xF3rio: enquanto a compra sem cadastro est\xE1 liberada, n\xE3o h\xE1 conta cujo e-mail confirmar." })
|
|
3877
|
-
]
|
|
3878
|
-
}
|
|
3879
|
-
),
|
|
3880
|
-
/* @__PURE__ */ jsx(SettingsGroup, { title: "Carrinho", description: "O que o comprador pode aplicar antes de pagar.", children: /* @__PURE__ */ jsx(
|
|
3881
|
-
ToggleRow,
|
|
3882
|
-
{
|
|
3883
|
-
label: "Cupons de desconto",
|
|
3884
|
-
description: "Mostrar o campo de c\xF3digo no carrinho e no checkout.",
|
|
3885
|
-
checked: value.discountsEnabled ?? true,
|
|
3886
|
-
onChange: (v) => set({ discountsEnabled: v })
|
|
3887
|
-
}
|
|
3888
|
-
) }),
|
|
3889
|
-
/* @__PURE__ */ jsx(
|
|
3890
|
-
SettingsGroup,
|
|
3891
|
-
{
|
|
3892
|
-
title: "Consentimento de marketing",
|
|
3893
|
-
description: "Coleta de opt-in para e-mails, no padr\xE3o que a LGPD pede: marcado por quem quer, nunca por padr\xE3o.",
|
|
3894
|
-
children: /* @__PURE__ */ jsx(
|
|
3895
|
-
ToggleRow,
|
|
3896
|
-
{
|
|
3897
|
-
label: "Mostrar caixa de opt-in",
|
|
3898
|
-
description: "Exibir a caixa \xABquero receber novidades\xBB no checkout, desmarcada.",
|
|
3899
|
-
checked: value.marketingOptIn ?? false,
|
|
3900
|
-
onChange: (v) => set({ marketingOptIn: v })
|
|
3901
|
-
}
|
|
3902
|
-
)
|
|
3903
|
-
}
|
|
3904
|
-
),
|
|
3905
|
-
/* @__PURE__ */ jsx(
|
|
3906
|
-
SettingsGroup,
|
|
3907
|
-
{
|
|
3908
|
-
title: "Campos do cliente",
|
|
3909
|
-
description: "O que o comprador v\xEA e o que ele precisa preencher. Campos travados s\xE3o os que a entrega n\xE3o dispensa.",
|
|
3910
|
-
children: FIELDS.map((field2) => /* @__PURE__ */ jsx(
|
|
3911
|
-
SelectRow,
|
|
3912
|
-
{
|
|
3913
|
-
label: field2.label,
|
|
3914
|
-
description: field2.hint,
|
|
3915
|
-
value: toRequirement(fields[field2.key], !!field2.locked),
|
|
3916
|
-
options: field2.locked ? [{ value: "required", label: "Pedir (obrigat\xF3rio)" }] : REQUIREMENT,
|
|
3917
|
-
onChange: (choice) => setField(field2.key, choice),
|
|
3918
|
-
disabled: field2.locked
|
|
3919
|
-
},
|
|
3920
|
-
field2.key
|
|
3921
|
-
))
|
|
3922
|
-
}
|
|
3923
|
-
)
|
|
3924
|
-
] });
|
|
3925
|
-
}
|
|
3926
|
-
function PackagesSettings({ basePath = shippingRegistryPath(PACKAGES_REGISTRY) }) {
|
|
3927
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
3928
|
-
/* @__PURE__ */ jsxs("div", { className: "px-1 pt-4", children: [
|
|
3929
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Embalagens" }),
|
|
3930
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: packagesRegistry.description })
|
|
3931
|
-
] }),
|
|
3932
|
-
/* @__PURE__ */ jsx(RegistryCrudView, { registry: packagesRegistry, basePath, embedded: true })
|
|
3933
|
-
] });
|
|
3934
|
-
}
|
|
3935
|
-
function HandlingSettings({ settings, provider }) {
|
|
3936
|
-
const shop = React14__default.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
|
|
3937
|
-
const { value, set, loading, supported } = settings;
|
|
3938
|
-
const [noteDraft, setNoteDraft] = React14__default.useState(null);
|
|
3939
|
-
const [profiles, setProfiles] = React14__default.useState(null);
|
|
3940
|
-
React14__default.useEffect(() => {
|
|
3941
|
-
let cancelled = false;
|
|
3942
|
-
void shop.listShippingProfiles?.().then((list) => {
|
|
3943
|
-
if (!cancelled) setProfiles(list);
|
|
3944
|
-
}).catch(() => {
|
|
3945
|
-
if (!cancelled) setProfiles([]);
|
|
3946
|
-
});
|
|
3947
|
-
return () => {
|
|
3948
|
-
cancelled = true;
|
|
3949
|
-
};
|
|
3950
|
-
}, [shop]);
|
|
3951
|
-
if (!supported) return null;
|
|
3952
|
-
const storedNote = value.handlingNote ?? "";
|
|
3953
|
-
const note = noteDraft ?? storedNote;
|
|
3954
|
-
const custom = (profiles ?? []).filter((p) => !p.isDefault);
|
|
3955
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3956
|
-
/* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
|
|
3957
|
-
/* @__PURE__ */ jsxs("h3", { className: "flex items-center gap-2 text-sm font-semibold", children: [
|
|
3958
|
-
/* @__PURE__ */ jsx(Layers, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
3959
|
-
"Produtos que v\xE3o de outro jeito"
|
|
3960
|
-
] }),
|
|
3961
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Um perfil \xE9 um grupo de produtos com zonas pr\xF3prias \u2014 refrigerados, fr\xE1geis, volumosos, sob encomenda. Um perfil com s\xF3 as suas zonas e o gateway desligado \xE9 como se diz \xABisto n\xE3o vai de transportadora\xBB. Produto sem perfil usa o geral, que \xE9 o de todos hoje." })
|
|
3962
|
-
] }),
|
|
3963
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
|
|
3964
|
-
Button,
|
|
3965
|
-
{
|
|
3966
|
-
variant: "bare",
|
|
3967
|
-
size: "bare",
|
|
3968
|
-
type: "button",
|
|
3969
|
-
onClick: () => adminNavigateTo(shippingRegistryPath(SHIPPING_PROFILES_REGISTRY)),
|
|
3970
|
-
className: "font-normal justify-start whitespace-normal flex w-full items-center gap-3 py-3.5 text-left",
|
|
3971
|
-
children: [
|
|
3972
|
-
/* @__PURE__ */ jsx(Layers, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
3973
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
3974
|
-
/* @__PURE__ */ jsx("span", { className: "block text-sm font-medium", children: profiles == null ? "Carregando perfis\u2026" : custom.length === 0 ? "S\xF3 o perfil geral" : `${custom.length} ${custom.length === 1 ? "perfil" : "perfis"} al\xE9m do geral` }),
|
|
3975
|
-
/* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: [
|
|
3976
|
-
custom.length === 0 ? "Todo produto entrega pelas mesmas zonas. Crie um perfil quando um grupo precisar de zonas diferentes." : custom.slice(0, 3).map((p) => p.name).join(" \xB7 "),
|
|
3977
|
-
custom.length > 3 && ` e ${custom.length - 3} mais`
|
|
3978
|
-
] })
|
|
3979
|
-
] }),
|
|
3980
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4 shrink-0 text-muted-foreground" })
|
|
3981
|
-
]
|
|
3982
|
-
}
|
|
3983
|
-
) }),
|
|
3984
|
-
/* @__PURE__ */ jsx(
|
|
3985
|
-
SettingsGroup,
|
|
3986
|
-
{
|
|
3987
|
-
title: "Recado para quem entrega",
|
|
3988
|
-
description: "Vai junto com o pedido \u2014 para voc\xEA, para o motoboy, e na observa\xE7\xE3o da etiqueta quando houver gateway. N\xE3o aparece para o cliente, e n\xE3o decide quem pode levar.",
|
|
3989
|
-
children: /* @__PURE__ */ jsx(
|
|
3990
|
-
TextRow,
|
|
3991
|
-
{
|
|
3992
|
-
label: "Instru\xE7\xE3o de manuseio",
|
|
3993
|
-
description: "Ex.: caixa t\xE9rmica com gelo, entregar em at\xE9 24h.",
|
|
3994
|
-
value: note,
|
|
3995
|
-
onChange: setNoteDraft,
|
|
3996
|
-
onBlurCommit: () => {
|
|
3997
|
-
const next = note.trim();
|
|
3998
|
-
if (next !== storedNote) set({ handlingNote: next });
|
|
3999
|
-
setNoteDraft(null);
|
|
4000
|
-
},
|
|
4001
|
-
placeholder: "Sem instru\xE7\xE3o",
|
|
4002
|
-
disabled: loading
|
|
4003
|
-
}
|
|
4004
|
-
)
|
|
4005
|
-
}
|
|
4006
|
-
)
|
|
4007
|
-
] });
|
|
4008
|
-
}
|
|
4009
|
-
function ChoiceCards({
|
|
4010
|
-
label,
|
|
4011
|
-
choices,
|
|
4012
|
-
value,
|
|
4013
|
-
onChange,
|
|
4014
|
-
disabled
|
|
4015
|
-
}) {
|
|
4016
|
-
return /* @__PURE__ */ jsx("div", { className: "space-y-2 py-3", role: "radiogroup", "aria-label": label, children: choices.map((choice) => {
|
|
4017
|
-
const chosen = value === choice.value;
|
|
4018
|
-
const Icon = choice.Icon;
|
|
4019
|
-
return /* @__PURE__ */ jsxs(
|
|
4020
|
-
Button,
|
|
4021
|
-
{
|
|
4022
|
-
variant: "bare",
|
|
4023
|
-
size: "bare",
|
|
4024
|
-
type: "button",
|
|
4025
|
-
role: "radio",
|
|
4026
|
-
"aria-checked": chosen,
|
|
4027
|
-
disabled,
|
|
4028
|
-
onClick: () => onChange(choice.value),
|
|
4029
|
-
className: `font-normal justify-start whitespace-normal flex w-full items-start gap-3 rounded-lg border p-3 text-left transition-colors ${chosen ? "border-primary bg-primary/5" : "border-border hover:bg-muted/40"} disabled:opacity-60`,
|
|
4030
|
-
children: [
|
|
4031
|
-
/* @__PURE__ */ jsx(
|
|
4032
|
-
"span",
|
|
4033
|
-
{
|
|
4034
|
-
"aria-hidden": true,
|
|
4035
|
-
className: `mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border ${chosen ? "border-primary" : "border-muted-foreground/40"}`,
|
|
4036
|
-
children: chosen && /* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full bg-primary" })
|
|
4037
|
-
}
|
|
4038
|
-
),
|
|
4039
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
4040
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
|
|
4041
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
4042
|
-
choice.label
|
|
4043
|
-
] }),
|
|
4044
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: choice.description })
|
|
4045
|
-
] })
|
|
4046
|
-
]
|
|
4047
|
-
},
|
|
4048
|
-
choice.value
|
|
4049
|
-
);
|
|
4050
|
-
}) });
|
|
4051
|
-
}
|
|
4052
|
-
var EMPTY = { rules: [], gaps: [] };
|
|
4053
|
-
var draft = EMPTY;
|
|
4054
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
4055
|
-
var seq = 0;
|
|
4056
|
-
var nextId = () => `rule-${++seq}`;
|
|
4057
|
-
var identity = (rule) => [rule.matchKind, rule.cityCode ?? "", rule.districtKey ?? "", rule.postalFrom ?? "", rule.postalTo ?? ""].join("|");
|
|
4058
|
-
function emit() {
|
|
4059
|
-
for (const listener of listeners) listener();
|
|
4060
|
-
}
|
|
4061
|
-
function getDeliveryDraft() {
|
|
4062
|
-
return draft;
|
|
4063
|
-
}
|
|
4064
|
-
function setDeliveryDraft(next) {
|
|
4065
|
-
draft = { ...next, rules: next.rules.map((rule) => ({ ...rule, id: rule.id || nextId() })) };
|
|
4066
|
-
emit();
|
|
4067
|
-
}
|
|
4068
|
-
function addDeliveryDraftRules(incoming) {
|
|
4069
|
-
const seen = new Set(draft.rules.map(identity));
|
|
4070
|
-
const added = [];
|
|
4071
|
-
for (const rule of incoming) {
|
|
4072
|
-
const key = identity(rule);
|
|
4073
|
-
if (seen.has(key)) continue;
|
|
4074
|
-
seen.add(key);
|
|
4075
|
-
added.push({ ...rule, id: nextId() });
|
|
4076
|
-
}
|
|
4077
|
-
if (added.length === 0) return 0;
|
|
4078
|
-
draft = { ...draft, rules: [...draft.rules, ...added] };
|
|
4079
|
-
emit();
|
|
4080
|
-
return added.length;
|
|
4081
|
-
}
|
|
4082
|
-
function isInDeliveryDraft(rule) {
|
|
4083
|
-
const key = identity(rule);
|
|
4084
|
-
return draft.rules.some((existing) => identity(existing) === key);
|
|
4085
|
-
}
|
|
4086
|
-
function setAllDeliveryDraftRates(patch) {
|
|
4087
|
-
let touched = 0;
|
|
4088
|
-
const rules = draft.rules.map((rule) => {
|
|
4089
|
-
if (rule.outcome === "refuse") return rule;
|
|
4090
|
-
touched++;
|
|
4091
|
-
return { ...rule, ...patch };
|
|
4092
|
-
});
|
|
4093
|
-
if (touched === 0) return 0;
|
|
4094
|
-
draft = { ...draft, rules };
|
|
4095
|
-
emit();
|
|
4096
|
-
return touched;
|
|
4097
|
-
}
|
|
4098
|
-
function updateDeliveryDraftRule(id, patch) {
|
|
4099
|
-
const index = draft.rules.findIndex((rule) => rule.id === id);
|
|
4100
|
-
if (index < 0) return false;
|
|
4101
|
-
const rules = draft.rules.slice();
|
|
4102
|
-
rules[index] = { ...rules[index], ...patch, id };
|
|
4103
|
-
draft = { ...draft, rules };
|
|
4104
|
-
emit();
|
|
4105
|
-
return true;
|
|
4106
|
-
}
|
|
4107
|
-
function removeDeliveryDraftRule(id) {
|
|
4108
|
-
const rules = draft.rules.filter((rule) => rule.id !== id);
|
|
4109
|
-
if (rules.length === draft.rules.length) return false;
|
|
4110
|
-
draft = { ...draft, rules };
|
|
4111
|
-
emit();
|
|
4112
|
-
return true;
|
|
4113
|
-
}
|
|
4114
|
-
function clearDeliveryDraft() {
|
|
4115
|
-
draft = EMPTY;
|
|
4116
|
-
emit();
|
|
4117
|
-
}
|
|
4118
|
-
function subscribeDeliveryDraft(listener) {
|
|
4119
|
-
listeners.add(listener);
|
|
4120
|
-
return () => {
|
|
4121
|
-
listeners.delete(listener);
|
|
4122
|
-
};
|
|
4123
|
-
}
|
|
4124
|
-
function useDeliveryDraft() {
|
|
4125
|
-
return React14.useSyncExternalStore(subscribeDeliveryDraft, getDeliveryDraft, getDeliveryDraft);
|
|
4126
|
-
}
|
|
4127
|
-
|
|
4128
|
-
// src/views/delivery-agent.ts
|
|
4129
|
-
var DELIVERY_AREAS_INSTRUCTIONS = `
|
|
4130
|
-
Esta \xE9 a tela de Entrega da loja. Aqui voc\xEA configura ONDE a loja entrega, ONDE ela recusa e onde precisa confirmar antes.
|
|
4131
|
-
|
|
4132
|
-
Uma regra tem duas partes: a \xC1REA (um munic\xEDpio ou um bairro) e o DESFECHO:
|
|
4133
|
-
\xB7 entrego (deliver) \xB7 confirmo antes (review) \xB7 n\xE3o entrego (refuse)
|
|
4134
|
-
Entre as regras que casam com um endere\xE7o, vence a mais estrita. Ent\xE3o "Zona Norte: entrego" + "Mar\xE9: n\xE3o entrego" s\xE3o DUAS regras, e a segunda ganha na Mar\xE9. Nunca tente recortar uma \xE1rea grande em peda\xE7os para abrir exce\xE7\xE3o \u2014 declare a exce\xE7\xE3o.
|
|
4135
|
-
|
|
4136
|
-
PREFIRA A \xC1REA MAIOR MENOS AS EXCE\xC7\xD5ES. \xC9 assim que o lojista fala, e \xE9 o que a regra mais estrita permite. Quando ele lista v\xE1rias zonas de uma mesma cidade \u2014 "zona norte, zona sul, zona oeste, zona sudoeste" \u2014 isso \xC9 a cidade inteira: proponha UMA regra de munic\xEDpio e as exce\xE7\xF5es, n\xE3o oitenta bairros. Uma whitelist de bairros \xE9 pior de duas formas: ela envelhece (bairro que faltar na lista vira venda recusada em sil\xEAncio) e ningu\xE9m confere oitenta linhas.
|
|
4137
|
-
|
|
4138
|
-
Quando a lojista colar uma mensagem descrevendo a opera\xE7\xE3o dela:
|
|
4139
|
-
1. Separe cada lugar citado e se ele \xE9 entrega, recusa ou d\xFAvida. "Evitar X", "exceto X" e "n\xE3o entregamos X" s\xE3o recusa.
|
|
4140
|
-
2. Chame \`findDeliveryAreas\` UMA vez com todos os nomes juntos.
|
|
4141
|
-
\xB7 \`resolved\` est\xE1 pronto para virar regra.
|
|
4142
|
-
\xB7 \`options\` \xE9 amb\xEDguo: pergunte qual, n\xE3o escolha.
|
|
4143
|
-
\xB7 \`notFound\` \xE9 o caso importante. Muita comunidade n\xE3o tem bairro nem CEP pr\xF3prio \u2014 divide o do bairro vizinho. N\xC3O invente faixa de CEP para ela. Diga isso em uma linha e ofere\xE7a o que existe: marcar o bairro que a cont\xE9m como "confirmo antes".
|
|
4144
|
-
\xB7 \`group\` (Zona Sul, Regi\xE3o dos Lagos) traz \`members\`. Use os membros como regras individuais S\xD3 quando ele citou uma parte da cidade. Se ele citou zonas que somam a cidade toda, jogue os grupos fora e use o munic\xEDpio.
|
|
4145
|
-
3. Chame \`proposeDeliveryRules\` com o que resolveu. Ele N\xC3O grava \u2014 desenha a tabela na tela dela.
|
|
4146
|
-
4. Pre\xE7o e prazo: s\xF3 preencha o que a mensagem disse. Se ela n\xE3o disse, deixe vazio e pergunte UMA vez, junto, no fim: "quanto voc\xEA cobra em cada regi\xE3o, e tem frete gr\xE1tis acima de algum valor?".
|
|
4147
|
-
5. Quando ela confirmar, chame \`commitDeliveryRules\`. Depois teste com \`checkDeliveryFor\` dois ou tr\xEAs CEPs (um que entrega, um que recusa) e relate o resultado em duas linhas.
|
|
4148
|
-
|
|
4149
|
-
Regras:
|
|
4150
|
-
\xB7 Voc\xEA NUNCA escreve um CEP ou um c\xF3digo de munic\xEDpio que n\xE3o tenha vindo de \`findDeliveryAreas\`.
|
|
4151
|
-
\xB7 N\xC3O proponha faixa de CEP. Faixa \xE9 como transportadora precifica, n\xE3o como uma pessoa descreve onde entrega, e ningu\xE9m no mercado pede isso ao lojista. Ela existe s\xF3 para quando a lojista estiver COPIANDO uma tabela de transportadora e disser os n\xFAmeros ela mesma. Se voc\xEA n\xE3o achar a \xE1rea pelo nome, pergunte \u2014 n\xE3o caia na faixa.
|
|
4152
|
-
\xB7 Comunidade que n\xE3o resolve pelo nome (Chapad\xE3o \xE9 o caso) n\xE3o vira recusa inventada: ofere\xE7a marcar o bairro que a cont\xE9m como "confirmo antes", que aceita o pedido e deixa a decis\xE3o com ele.
|
|
4153
|
-
\xB7 N\xE3o imprima a tabela inteira no texto: ela est\xE1 na tela. Diga quantas regras entraram, quantas s\xE3o recusa, e o que ficou em aberto.
|
|
4154
|
-
\xB7 Portugu\xEAs do Brasil, curto.
|
|
4155
|
-
`.trim();
|
|
4156
|
-
var FIND_AREAS = {
|
|
4157
|
-
id: "shop.delivery.find-areas",
|
|
4158
|
-
name: "findDeliveryAreas",
|
|
4159
|
-
description: 'Resolves place names the merchant wrote ("Angra", "Zona Sul", "Barra da Tijuca", "Itacuru\xE7\xE1") into real municipalities (IBGE code), neighbourhoods, or curated groups that expand into several of them. Send EVERY name of the message in one call. This is the ONLY source of a municipality code or a neighbourhood key \u2014 never write one that did not come from here. A name that answers `notFound` is a question for the merchant, not a rule to invent.',
|
|
4160
|
-
icon: "MapPin",
|
|
4161
|
-
mode: "read",
|
|
4162
|
-
execution: { plane: "client" },
|
|
4163
|
-
category: "Shop",
|
|
4164
|
-
role: "operations",
|
|
4165
|
-
parameters: {
|
|
4166
|
-
type: "object",
|
|
4167
|
-
properties: {
|
|
4168
|
-
queries: {
|
|
4169
|
-
type: "array",
|
|
4170
|
-
description: 'The place names as the merchant wrote them, without "n\xE3o entrego" / "exceto".',
|
|
4171
|
-
items: { type: "string" }
|
|
4172
|
-
}
|
|
4173
|
-
},
|
|
4174
|
-
required: ["queries"]
|
|
4175
|
-
}
|
|
4176
|
-
};
|
|
4177
|
-
var PROPOSE_RULES = {
|
|
4178
|
-
id: "shop.delivery.propose-rules",
|
|
4179
|
-
name: "proposeDeliveryRules",
|
|
4180
|
-
description: "Draws the delivery rules the message describes as an EDITABLE TABLE on the merchant's screen. Writes nothing. Every area must carry the ids findDeliveryAreas answered. A group is expanded into one rule per member here, so the merchant sees and trims what it covers. Leave rate and ETA out when the message did not state them.",
|
|
4181
|
-
icon: "ListChecks",
|
|
4182
|
-
mode: "read",
|
|
4183
|
-
execution: { plane: "client" },
|
|
4184
|
-
category: "Shop",
|
|
4185
|
-
role: "operations",
|
|
4186
|
-
parameters: {
|
|
4187
|
-
type: "object",
|
|
4188
|
-
properties: {
|
|
4189
|
-
rules: {
|
|
4190
|
-
type: "array",
|
|
4191
|
-
description: "One entry per area named in the message.",
|
|
4192
|
-
items: {
|
|
4193
|
-
type: "object",
|
|
4194
|
-
properties: {
|
|
4195
|
-
kind: {
|
|
4196
|
-
type: "string",
|
|
4197
|
-
enum: ["city", "district", "group", "postal_range"],
|
|
4198
|
-
description: "The `kind` findDeliveryAreas answered. Use postal_range ONLY when the merchant is copying a carrier freight table and dictated the numbers \u2014 never to describe where they deliver."
|
|
4199
|
-
},
|
|
4200
|
-
label: { type: "string", description: "The place name, as it should read on the table." },
|
|
4201
|
-
cityCode: { type: "string", description: "IBGE code from findDeliveryAreas." },
|
|
4202
|
-
districtKey: { type: "string", description: "Neighbourhood key from findDeliveryAreas." },
|
|
4203
|
-
postalFrom: { type: "string", description: "Only for postal_range." },
|
|
4204
|
-
postalTo: { type: "string", description: "Only for postal_range." },
|
|
4205
|
-
outcome: {
|
|
4206
|
-
type: "string",
|
|
4207
|
-
enum: ["deliver", "review", "refuse"],
|
|
4208
|
-
description: "deliver | review (confirm before dispatch) | refuse."
|
|
4209
|
-
},
|
|
4210
|
-
rate: { type: "number", description: "Only if the message stated a price. Otherwise omit." },
|
|
4211
|
-
freeAbove: { type: "number", description: "Only if the message stated a threshold." },
|
|
4212
|
-
etaMinDays: { type: "number" },
|
|
4213
|
-
etaMaxDays: { type: "number" },
|
|
4214
|
-
source: { type: "string", description: "The words of the message this rule came from." }
|
|
4215
|
-
},
|
|
4216
|
-
required: ["kind", "label", "outcome"]
|
|
4217
|
-
}
|
|
4218
|
-
},
|
|
4219
|
-
gaps: {
|
|
4220
|
-
type: "array",
|
|
4221
|
-
description: "What the message named and you could NOT resolve, so the merchant sees it too.",
|
|
4222
|
-
items: {
|
|
4223
|
-
type: "object",
|
|
4224
|
-
properties: {
|
|
4225
|
-
query: { type: "string" },
|
|
4226
|
-
reason: { type: "string", description: "Plain Portuguese, one line." }
|
|
4227
|
-
},
|
|
4228
|
-
required: ["query", "reason"]
|
|
4229
|
-
}
|
|
4230
|
-
}
|
|
4231
|
-
},
|
|
4232
|
-
required: ["rules"]
|
|
4233
|
-
}
|
|
4234
|
-
};
|
|
4235
|
-
var SET_RULE = {
|
|
4236
|
-
id: "shop.delivery.set-rule",
|
|
4237
|
-
name: "setDeliveryRule",
|
|
4238
|
-
description: "Corrects one line of the draft table already on screen \u2014 its outcome, price, free-above or ETA. Use when the merchant answers a question about the rules you proposed, instead of rebuilding the whole draft.",
|
|
4239
|
-
icon: "Pencil",
|
|
4240
|
-
mode: "read",
|
|
4241
|
-
execution: { plane: "client" },
|
|
4242
|
-
category: "Shop",
|
|
4243
|
-
role: "operations",
|
|
4244
|
-
parameters: {
|
|
4245
|
-
type: "object",
|
|
4246
|
-
properties: {
|
|
4247
|
-
id: { type: "string", description: "The rule id proposeDeliveryRules answered." },
|
|
4248
|
-
outcome: { type: "string", enum: ["deliver", "review", "refuse"] },
|
|
4249
|
-
rate: { type: "number" },
|
|
4250
|
-
freeAbove: { type: "number" },
|
|
4251
|
-
etaMinDays: { type: "number" },
|
|
4252
|
-
etaMaxDays: { type: "number" }
|
|
4253
|
-
},
|
|
4254
|
-
required: ["id"]
|
|
4255
|
-
}
|
|
4256
|
-
};
|
|
4257
|
-
var COMMIT_RULES = {
|
|
4258
|
-
id: "shop.delivery.commit-rules",
|
|
4259
|
-
name: "commitDeliveryRules",
|
|
4260
|
-
description: "Saves the draft table as the store's delivery rules. Call ONLY after the merchant said the table is right. It changes who can buy: an address outside every rule cannot check out, and a refused one is blocked server-side. Saves the draft as it stands on screen \u2014 the merchant may have edited it, and those edits win.",
|
|
4261
|
-
icon: "Save",
|
|
4262
|
-
mode: "persist",
|
|
4263
|
-
execution: { plane: "client" },
|
|
4264
|
-
category: "Shop",
|
|
4265
|
-
role: "operations",
|
|
4266
|
-
permission: { feature: "shop", action: "update" },
|
|
4267
|
-
parameters: { type: "object", properties: {} }
|
|
4268
|
-
};
|
|
4269
|
-
var CHECK_DELIVERY = {
|
|
4270
|
-
id: "shop.delivery.check",
|
|
4271
|
-
name: "checkDeliveryFor",
|
|
4272
|
-
description: 'Answers what the store does with a given postcode right now: deliver, confirm first, refuse, or outside every rule. Use it to CHECK YOUR OWN WORK after saving, and to answer "why could this customer not buy?".',
|
|
4273
|
-
icon: "Search",
|
|
4274
|
-
mode: "read",
|
|
4275
|
-
execution: { plane: "client" },
|
|
4276
|
-
category: "Shop",
|
|
4277
|
-
role: "operations",
|
|
4278
|
-
parameters: {
|
|
4279
|
-
type: "object",
|
|
4280
|
-
properties: {
|
|
4281
|
-
postalCodes: {
|
|
4282
|
-
type: "array",
|
|
4283
|
-
description: "One or more CEPs, with or without punctuation.",
|
|
4284
|
-
items: { type: "string" }
|
|
4285
|
-
}
|
|
4286
|
-
},
|
|
4287
|
-
required: ["postalCodes"]
|
|
4288
|
-
}
|
|
4289
|
-
};
|
|
4290
|
-
[
|
|
4291
|
-
FIND_AREAS.id,
|
|
4292
|
-
PROPOSE_RULES.id,
|
|
4293
|
-
SET_RULE.id,
|
|
4294
|
-
COMMIT_RULES.id,
|
|
4295
|
-
CHECK_DELIVERY.id
|
|
4296
|
-
];
|
|
4297
|
-
var text2 = (value) => typeof value === "string" ? value.trim() : "";
|
|
4298
|
-
var number = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
4299
|
-
var OUTCOMES = ["deliver", "review", "refuse"];
|
|
4300
|
-
var outcomeOf = (value) => OUTCOMES.includes(value) ? value : "deliver";
|
|
4301
|
-
function candidateOut(area) {
|
|
4302
|
-
return {
|
|
4303
|
-
kind: area.kind,
|
|
4304
|
-
label: area.label,
|
|
4305
|
-
cityCode: area.cityCode ?? void 0,
|
|
4306
|
-
cityName: area.cityName ?? void 0,
|
|
4307
|
-
state: area.state ?? void 0,
|
|
4308
|
-
districtKey: area.districtKey ?? void 0,
|
|
4309
|
-
knownPostalCodes: area.knownPostalCodes || void 0,
|
|
4310
|
-
members: area.members?.map((m) => ({
|
|
4311
|
-
kind: m.kind,
|
|
4312
|
-
label: m.label,
|
|
4313
|
-
cityCode: m.cityCode ?? void 0,
|
|
4314
|
-
districtKey: m.districtKey ?? void 0
|
|
4315
|
-
}))
|
|
4316
|
-
};
|
|
4317
|
-
}
|
|
4318
|
-
function pickCandidate(query, areas) {
|
|
4319
|
-
if (areas.length === 0) return {};
|
|
4320
|
-
if (areas.length === 1) return { resolved: areas[0] };
|
|
4321
|
-
const key = query.trim().toLowerCase();
|
|
4322
|
-
const exact = areas.filter((a) => a.label.trim().toLowerCase() === key);
|
|
4323
|
-
if (exact.length === 1) return { resolved: exact[0] };
|
|
4324
|
-
return { options: areas.slice(0, 6) };
|
|
4325
|
-
}
|
|
4326
|
-
function ruleFromArgs(raw, index) {
|
|
4327
|
-
const kind = text2(raw.kind);
|
|
4328
|
-
const label = text2(raw.label);
|
|
4329
|
-
if (!label) return null;
|
|
4330
|
-
const base = {
|
|
4331
|
-
id: `draft-${index}`,
|
|
4332
|
-
label,
|
|
4333
|
-
outcome: outcomeOf(raw.outcome),
|
|
4334
|
-
rate: number(raw.rate),
|
|
4335
|
-
freeAbove: number(raw.freeAbove),
|
|
4336
|
-
etaMinDays: number(raw.etaMinDays),
|
|
4337
|
-
etaMaxDays: number(raw.etaMaxDays),
|
|
4338
|
-
source: text2(raw.source) || void 0
|
|
4339
|
-
};
|
|
4340
|
-
if (kind === "city") {
|
|
4341
|
-
const cityCode = text2(raw.cityCode);
|
|
4342
|
-
if (!cityCode) return null;
|
|
4343
|
-
return { ...base, matchKind: "city", cityCode };
|
|
4344
|
-
}
|
|
4345
|
-
if (kind === "district") {
|
|
4346
|
-
const districtKey = text2(raw.districtKey);
|
|
4347
|
-
if (!districtKey) return null;
|
|
4348
|
-
return {
|
|
4349
|
-
...base,
|
|
4350
|
-
matchKind: "district",
|
|
4351
|
-
districtKey,
|
|
4352
|
-
cityCode: text2(raw.cityCode) || null
|
|
4353
|
-
};
|
|
4354
|
-
}
|
|
4355
|
-
if (kind === "postal_range") {
|
|
4356
|
-
const from = text2(raw.postalFrom).replace(/\D/g, "");
|
|
4357
|
-
const to = text2(raw.postalTo).replace(/\D/g, "");
|
|
4358
|
-
if (from.length !== 8 || to.length !== 8) return null;
|
|
4359
|
-
return { ...base, matchKind: "postal_range", postalFrom: from, postalTo: to };
|
|
4360
|
-
}
|
|
4361
|
-
return null;
|
|
4362
|
-
}
|
|
4363
|
-
function zoneInputFrom(rule) {
|
|
4364
|
-
return {
|
|
4365
|
-
name: rule.label,
|
|
4366
|
-
carrier: null,
|
|
4367
|
-
matchKind: rule.matchKind,
|
|
4368
|
-
cityCode: rule.cityCode ?? null,
|
|
4369
|
-
districtKey: rule.districtKey ?? null,
|
|
4370
|
-
outcome: rule.outcome,
|
|
4371
|
-
postalFrom: rule.postalFrom ?? null,
|
|
4372
|
-
postalTo: rule.postalTo ?? null,
|
|
4373
|
-
// Uma regra de recusa não cobra nada: o preço dela nunca é lido.
|
|
4374
|
-
rate: rule.outcome === "refuse" ? 0 : rule.rate ?? 0,
|
|
4375
|
-
freeAbove: rule.freeAbove ?? null,
|
|
4376
|
-
etaMinDays: rule.etaMinDays ?? null,
|
|
4377
|
-
etaMaxDays: rule.etaMaxDays ?? null,
|
|
4378
|
-
active: true,
|
|
4379
|
-
sortOrder: 0,
|
|
4380
|
-
excluded: rule.outcome === "refuse"
|
|
4381
|
-
};
|
|
4382
|
-
}
|
|
4383
|
-
function createDeliveryAreaTools({ getProvider, onSaved }) {
|
|
4384
|
-
const failed = (error) => ({ ok: false, error: errorMessage(error) });
|
|
4385
|
-
return [
|
|
4386
|
-
{
|
|
4387
|
-
tool: FIND_AREAS,
|
|
4388
|
-
execute: async (args) => {
|
|
4389
|
-
const queries = (Array.isArray(args.queries) ? args.queries : []).map(text2).filter(Boolean).slice(0, 30);
|
|
4390
|
-
if (queries.length === 0) return { ok: false, error: "no_queries" };
|
|
4391
|
-
try {
|
|
4392
|
-
const shop = getProvider();
|
|
4393
|
-
if (typeof shop.findAreas !== "function") {
|
|
4394
|
-
return { ok: false, error: "area_lookup_unavailable" };
|
|
4395
|
-
}
|
|
4396
|
-
const results = await Promise.all(
|
|
4397
|
-
queries.map(async (query) => {
|
|
4398
|
-
const areas = await shop.findAreas(query, 8).catch(() => []);
|
|
4399
|
-
const { resolved, options } = pickCandidate(query, areas);
|
|
4400
|
-
if (resolved) return { query, resolved: candidateOut(resolved) };
|
|
4401
|
-
if (options) return { query, options: options.map(candidateOut) };
|
|
4402
|
-
return {
|
|
4403
|
-
query,
|
|
4404
|
-
notFound: true,
|
|
4405
|
-
// Dito aqui e não deixado para o modelo deduzir: é a frase que
|
|
4406
|
-
// separa "não existe" de "existe mas não dá para separar por CEP".
|
|
4407
|
-
hint: "Sem munic\xEDpio, bairro ou grupo com esse nome. Comunidade normalmente n\xE3o tem CEP pr\xF3prio \u2014 divide o do bairro vizinho. Pergunte \xE0 lojista qual bairro a cont\xE9m."
|
|
4408
|
-
};
|
|
4409
|
-
})
|
|
4410
|
-
);
|
|
4411
|
-
return { ok: true, results };
|
|
4412
|
-
} catch (error) {
|
|
4413
|
-
return failed(error);
|
|
4414
|
-
}
|
|
4415
|
-
}
|
|
4416
|
-
},
|
|
4417
|
-
{
|
|
4418
|
-
tool: PROPOSE_RULES,
|
|
4419
|
-
execute: async (args) => {
|
|
4420
|
-
const raw = Array.isArray(args.rules) ? args.rules : [];
|
|
4421
|
-
const rules = [];
|
|
4422
|
-
const gaps = (Array.isArray(args.gaps) ? args.gaps : []).map((g) => ({ query: text2(g?.query), reason: text2(g?.reason) })).filter((g) => g.query);
|
|
4423
|
-
raw.forEach((entry, index) => {
|
|
4424
|
-
const rule = ruleFromArgs(entry ?? {}, rules.length);
|
|
4425
|
-
if (rule) {
|
|
4426
|
-
rules.push(rule);
|
|
4427
|
-
return;
|
|
4428
|
-
}
|
|
4429
|
-
gaps.push({
|
|
4430
|
-
query: text2(entry?.label) || `regra ${index + 1}`,
|
|
4431
|
-
reason: "Faltou o identificador da \xE1rea. Chame findDeliveryAreas para este nome e use o que ele responder."
|
|
4432
|
-
});
|
|
4433
|
-
});
|
|
4434
|
-
if (rules.length === 0 && gaps.length === 0) return { ok: false, error: "empty_draft" };
|
|
4435
|
-
setDeliveryDraft({ rules, gaps });
|
|
4436
|
-
return {
|
|
4437
|
-
ok: true,
|
|
4438
|
-
drafted: rules.length,
|
|
4439
|
-
refusing: rules.filter((r) => r.outcome === "refuse").length,
|
|
4440
|
-
reviewing: rules.filter((r) => r.outcome === "review").length,
|
|
4441
|
-
missingPrice: rules.filter((r) => r.outcome !== "refuse" && r.rate == null).length,
|
|
4442
|
-
gaps,
|
|
4443
|
-
rules: rules.map((r) => ({ id: r.id, label: r.label, outcome: r.outcome })),
|
|
4444
|
-
nextStep: "commitDeliveryRules"
|
|
4445
|
-
};
|
|
4446
|
-
}
|
|
4447
|
-
},
|
|
4448
|
-
{
|
|
4449
|
-
tool: SET_RULE,
|
|
4450
|
-
execute: async (args) => {
|
|
4451
|
-
const id = text2(args.id);
|
|
4452
|
-
if (!id) return { ok: false, error: "no_id" };
|
|
4453
|
-
const patch = {};
|
|
4454
|
-
if (args.outcome !== void 0) patch.outcome = outcomeOf(args.outcome);
|
|
4455
|
-
if (args.rate !== void 0) patch.rate = number(args.rate);
|
|
4456
|
-
if (args.freeAbove !== void 0) patch.freeAbove = number(args.freeAbove);
|
|
4457
|
-
if (args.etaMinDays !== void 0) patch.etaMinDays = number(args.etaMinDays);
|
|
4458
|
-
if (args.etaMaxDays !== void 0) patch.etaMaxDays = number(args.etaMaxDays);
|
|
4459
|
-
if (Object.keys(patch).length === 0) return { ok: false, error: "nothing_to_change" };
|
|
4460
|
-
if (!updateDeliveryDraftRule(id, patch)) return { ok: false, error: "rule_not_in_draft" };
|
|
4461
|
-
return { ok: true, id, ...patch };
|
|
4462
|
-
}
|
|
4463
|
-
},
|
|
4464
|
-
{
|
|
4465
|
-
tool: COMMIT_RULES,
|
|
4466
|
-
execute: async () => {
|
|
4467
|
-
const { rules } = getDeliveryDraft();
|
|
4468
|
-
if (rules.length === 0) return { ok: false, error: "nothing_to_save" };
|
|
4469
|
-
try {
|
|
4470
|
-
const shop = getProvider();
|
|
4471
|
-
if (typeof shop.createShippingZone !== "function") {
|
|
4472
|
-
return { ok: false, error: "zones_unavailable" };
|
|
4473
|
-
}
|
|
4474
|
-
const saved = [];
|
|
4475
|
-
const rejected = [];
|
|
4476
|
-
for (const rule of rules) {
|
|
4477
|
-
try {
|
|
4478
|
-
await shop.createShippingZone(zoneInputFrom(rule));
|
|
4479
|
-
saved.push(rule.label);
|
|
4480
|
-
} catch (error) {
|
|
4481
|
-
rejected.push({ label: rule.label, error: errorMessage(error) });
|
|
4482
|
-
}
|
|
4483
|
-
}
|
|
4484
|
-
if (saved.length > 0) {
|
|
4485
|
-
clearDeliveryDraft();
|
|
4486
|
-
onSaved();
|
|
4487
|
-
}
|
|
4488
|
-
return { ok: rejected.length === 0, saved: saved.length, rejected };
|
|
4489
|
-
} catch (error) {
|
|
4490
|
-
return failed(error);
|
|
4491
|
-
}
|
|
4492
|
-
}
|
|
4493
|
-
},
|
|
4494
|
-
{
|
|
4495
|
-
tool: CHECK_DELIVERY,
|
|
4496
|
-
execute: async (args) => {
|
|
4497
|
-
const codes = (Array.isArray(args.postalCodes) ? args.postalCodes : []).map((c) => text2(c).replace(/\D/g, "")).filter((c) => c.length === 8).slice(0, 10);
|
|
4498
|
-
if (codes.length === 0) return { ok: false, error: "no_postal_codes" };
|
|
4499
|
-
try {
|
|
4500
|
-
const shop = getProvider();
|
|
4501
|
-
if (typeof shop.checkDelivery !== "function") {
|
|
4502
|
-
return { ok: false, error: "delivery_check_unavailable" };
|
|
4503
|
-
}
|
|
4504
|
-
const results = await Promise.all(
|
|
4505
|
-
codes.map(async (code) => {
|
|
4506
|
-
const check = await shop.checkDelivery(code);
|
|
4507
|
-
return {
|
|
4508
|
-
postalCode: code,
|
|
4509
|
-
// null = nenhuma regra casou: fora de área, que não é o mesmo
|
|
4510
|
-
// que recusado e o texto para a lojista é outro.
|
|
4511
|
-
outcome: check.outcome ?? null,
|
|
4512
|
-
rule: check.zoneName ?? check.refusedZone ?? null,
|
|
4513
|
-
outsideEveryRule: !check.refused && !check.ownZone
|
|
4514
|
-
};
|
|
4515
|
-
})
|
|
4516
|
-
);
|
|
4517
|
-
return { ok: true, results };
|
|
4518
|
-
} catch (error) {
|
|
4519
|
-
return failed(error);
|
|
4520
|
-
}
|
|
4521
|
-
}
|
|
4522
|
-
}
|
|
4523
|
-
];
|
|
4524
|
-
}
|
|
4525
|
-
|
|
4526
|
-
// src/views/DeliveryAgentSurface.tsx
|
|
4527
|
-
function DeliveryAgentSurface({ provider, zones, onSaved }) {
|
|
4528
|
-
const live = React14.useRef({ provider, zones, onSaved });
|
|
4529
|
-
live.current = { provider, zones, onSaved };
|
|
4530
|
-
const contribution = React14.useMemo(() => ({
|
|
4531
|
-
id: "shop.delivery.settings",
|
|
4532
|
-
pluginId: "shop",
|
|
4533
|
-
role: "operations",
|
|
4534
|
-
instructions: DELIVERY_AREAS_INSTRUCTIONS,
|
|
4535
|
-
describe: () => {
|
|
4536
|
-
const { zones: rows } = live.current;
|
|
4537
|
-
const draft2 = getDeliveryDraft();
|
|
4538
|
-
const byOutcome = (outcome) => rows.filter((z) => z.active && (z.outcome ?? (z.excluded ? "refuse" : "deliver")) === outcome);
|
|
4539
|
-
return {
|
|
4540
|
-
title: "Configura\xE7\xE3o \u2014 Entrega",
|
|
4541
|
-
state: {
|
|
4542
|
-
screen: "/settings/shop-shipping",
|
|
4543
|
-
savedRules: {
|
|
4544
|
-
delivering: byOutcome("deliver").length,
|
|
4545
|
-
reviewing: byOutcome("review").length,
|
|
4546
|
-
refusing: byOutcome("refuse").length,
|
|
4547
|
-
// Os nomes, não as faixas: o agente precisa saber o que JÁ existe
|
|
4548
|
-
// para não propor a mesma região duas vezes.
|
|
4549
|
-
names: rows.filter((z) => z.active).slice(0, 40).map((z) => z.name)
|
|
4550
|
-
},
|
|
4551
|
-
draft: draft2.rules.length === 0 && draft2.gaps.length === 0 ? null : {
|
|
4552
|
-
rules: draft2.rules.map((r) => ({
|
|
4553
|
-
id: r.id,
|
|
4554
|
-
label: r.label,
|
|
4555
|
-
outcome: r.outcome,
|
|
4556
|
-
rate: r.rate ?? null,
|
|
4557
|
-
freeAbove: r.freeAbove ?? null
|
|
4558
|
-
})),
|
|
4559
|
-
gaps: draft2.gaps
|
|
4560
|
-
}
|
|
4561
|
-
}
|
|
4562
|
-
};
|
|
4563
|
-
},
|
|
4564
|
-
tools: createDeliveryAreaTools({
|
|
4565
|
-
getProvider: () => live.current.provider(),
|
|
4566
|
-
onSaved: () => live.current.onSaved()
|
|
4567
|
-
})
|
|
4568
|
-
}), []);
|
|
4569
|
-
useAgentSurface(contribution);
|
|
4570
|
-
return null;
|
|
4571
|
-
}
|
|
4572
|
-
var MIN_QUERY = 2;
|
|
4573
|
-
var DEBOUNCE_MS2 = 250;
|
|
4574
|
-
function ruleFor(area, outcome) {
|
|
4575
|
-
if (area.kind === "city" && area.cityCode) {
|
|
4576
|
-
return { matchKind: "city", label: area.label, cityCode: area.cityCode, outcome };
|
|
4577
|
-
}
|
|
4578
|
-
if (area.kind === "district" && area.districtKey) {
|
|
4579
|
-
return {
|
|
4580
|
-
matchKind: "district",
|
|
4581
|
-
label: area.label,
|
|
4582
|
-
districtKey: area.districtKey,
|
|
4583
|
-
cityCode: area.cityCode ?? null,
|
|
4584
|
-
outcome
|
|
4585
|
-
};
|
|
4586
|
-
}
|
|
4587
|
-
return null;
|
|
4588
|
-
}
|
|
4589
|
-
function rulesFor(area, outcome) {
|
|
4590
|
-
if (area.kind !== "group") {
|
|
4591
|
-
const single = ruleFor(area, outcome);
|
|
4592
|
-
return single ? [single] : [];
|
|
4593
|
-
}
|
|
4594
|
-
return (area.members ?? []).flatMap((member) => {
|
|
4595
|
-
const rule = ruleFor(
|
|
4596
|
-
{ kind: member.kind, label: member.label, cityCode: member.cityCode, districtKey: member.districtKey },
|
|
4597
|
-
outcome
|
|
4598
|
-
);
|
|
4599
|
-
return rule ? [rule] : [];
|
|
4600
|
-
});
|
|
4601
|
-
}
|
|
4602
|
-
function areaMeta(area) {
|
|
4603
|
-
if (area.matchedAs) return area.matchedAs;
|
|
4604
|
-
if (area.kind === "group") {
|
|
4605
|
-
const n = area.members?.length ?? 0;
|
|
4606
|
-
return `${n} ${n === 1 ? "regi\xE3o" : "regi\xF5es"}${area.cityName ? ` \xB7 ${area.cityName}` : ""}`;
|
|
4607
|
-
}
|
|
4608
|
-
if (area.kind === "city") return `Cidade inteira${area.state ? ` \xB7 ${area.state}` : ""}`;
|
|
4609
|
-
return `Bairro${area.cityName ? ` \xB7 ${area.cityName}` : ""}`;
|
|
4610
|
-
}
|
|
4611
|
-
var areaKeyOf = (area) => `${area.kind}:${area.districtKey ?? area.cityCode ?? area.label}`;
|
|
4612
|
-
function AreaSearch({ provider, outcome, placeholder, onlyCityCodes, emptyHint }) {
|
|
4613
|
-
const [query, setQuery] = React14__default.useState("");
|
|
4614
|
-
const [results, setResults] = React14__default.useState(null);
|
|
4615
|
-
const [loading, setLoading] = React14__default.useState(false);
|
|
4616
|
-
const [unavailable, setUnavailable] = React14__default.useState(false);
|
|
4617
|
-
const [justAdded, setJustAdded] = React14__default.useState(null);
|
|
4618
|
-
React14__default.useEffect(() => {
|
|
4619
|
-
const text4 = query.trim();
|
|
4620
|
-
if (text4.length < MIN_QUERY) {
|
|
4621
|
-
setResults(null);
|
|
4622
|
-
return void 0;
|
|
4623
|
-
}
|
|
4624
|
-
let cancelled = false;
|
|
4625
|
-
setLoading(true);
|
|
4626
|
-
const timer = setTimeout(() => {
|
|
4627
|
-
const shop = provider ? resolve(provider) : getShopProvider();
|
|
4628
|
-
if (typeof shop.findAreas !== "function") {
|
|
4629
|
-
if (!cancelled) {
|
|
4630
|
-
setUnavailable(true);
|
|
4631
|
-
setLoading(false);
|
|
4632
|
-
}
|
|
4633
|
-
return;
|
|
4634
|
-
}
|
|
4635
|
-
void shop.findAreas(text4, 10).then((list) => {
|
|
4636
|
-
if (!cancelled) {
|
|
4637
|
-
setResults(list);
|
|
4638
|
-
setUnavailable(false);
|
|
4639
|
-
}
|
|
4640
|
-
}).catch(() => {
|
|
4641
|
-
if (!cancelled) {
|
|
4642
|
-
setResults([]);
|
|
4643
|
-
setUnavailable(true);
|
|
4644
|
-
}
|
|
4645
|
-
}).finally(() => {
|
|
4646
|
-
if (!cancelled) setLoading(false);
|
|
4647
|
-
});
|
|
4648
|
-
}, DEBOUNCE_MS2);
|
|
4649
|
-
return () => {
|
|
4650
|
-
cancelled = true;
|
|
4651
|
-
clearTimeout(timer);
|
|
4652
|
-
};
|
|
4653
|
-
}, [query, provider]);
|
|
4654
|
-
const add = (area) => {
|
|
4655
|
-
const rules = rulesFor(area, outcome);
|
|
4656
|
-
if (rules.length === 0) return;
|
|
4657
|
-
const added = addDeliveryDraftRules(rules);
|
|
4658
|
-
setJustAdded(areaKeyOf(area));
|
|
4659
|
-
setTimeout(() => setJustAdded(null), 1400);
|
|
4660
|
-
if (added > 0) setQuery("");
|
|
4661
|
-
};
|
|
4662
|
-
const visible = (results ?? []).filter((area) => {
|
|
4663
|
-
if (!onlyCityCodes || onlyCityCodes.length === 0) return true;
|
|
4664
|
-
if (area.kind === "city") return true;
|
|
4665
|
-
return !area.cityCode || onlyCityCodes.includes(area.cityCode);
|
|
4666
|
-
});
|
|
4667
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4668
|
-
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4669
|
-
/* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
|
|
4670
|
-
/* @__PURE__ */ jsx(
|
|
4671
|
-
Input,
|
|
4672
|
-
{
|
|
4673
|
-
value: query,
|
|
4674
|
-
onChange: (e) => setQuery(e.target.value),
|
|
4675
|
-
placeholder,
|
|
4676
|
-
className: "h-auto rounded-lg border-border bg-background py-2 pl-9 pr-9"
|
|
4677
|
-
}
|
|
4678
|
-
),
|
|
4679
|
-
loading && /* @__PURE__ */ jsx(Loader2, { className: "absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-muted-foreground" })
|
|
4680
|
-
] }),
|
|
4681
|
-
results !== null && /* @__PURE__ */ jsxs("ul", { className: "mt-2 max-h-72 divide-y divide-border overflow-y-auto rounded-lg border border-border", children: [
|
|
4682
|
-
visible.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-3 py-6 text-center text-xs text-muted-foreground", children: unavailable ? "A busca de regi\xF5es ainda n\xE3o est\xE1 dispon\xEDvel neste banco." : emptyHint }),
|
|
4683
|
-
visible.map((area) => {
|
|
4684
|
-
const key = areaKeyOf(area);
|
|
4685
|
-
const already = area.kind !== "group" && isInDeliveryDraft({
|
|
4686
|
-
matchKind: area.kind === "city" ? "city" : "district",
|
|
4687
|
-
cityCode: area.cityCode ?? null,
|
|
4688
|
-
districtKey: area.districtKey ?? null
|
|
4689
|
-
});
|
|
4690
|
-
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
4691
|
-
Button,
|
|
4692
|
-
{
|
|
4693
|
-
variant: "bare",
|
|
4694
|
-
size: "bare",
|
|
4695
|
-
type: "button",
|
|
4696
|
-
onClick: () => add(area),
|
|
4697
|
-
disabled: already,
|
|
4698
|
-
className: "font-normal justify-start rounded-none whitespace-normal flex w-full items-center gap-3 px-3 py-2.5 text-left transition hover:bg-muted/50 disabled:opacity-50",
|
|
4699
|
-
children: [
|
|
4700
|
-
area.kind === "city" ? /* @__PURE__ */ jsx(Building2, { className: "h-4 w-4 shrink-0 text-muted-foreground" }) : /* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
4701
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
4702
|
-
/* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-medium", children: area.label }),
|
|
4703
|
-
/* @__PURE__ */ jsxs("span", { className: "block text-xs text-muted-foreground", children: [
|
|
4704
|
-
areaMeta(area),
|
|
4705
|
-
area.kind === "district" && !area.knownPostalCodes && " \xB7 ainda sem CEP visto"
|
|
4706
|
-
] })
|
|
4707
|
-
] }),
|
|
4708
|
-
justAdded === key ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 shrink-0 text-emerald-600" }) : already ? /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: "j\xE1 escolhido" }) : /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 shrink-0 text-muted-foreground" })
|
|
4709
|
-
]
|
|
4710
|
-
}
|
|
4711
|
-
) }, key);
|
|
4712
|
-
})
|
|
4713
|
-
] })
|
|
4714
|
-
] });
|
|
4715
|
-
}
|
|
4716
|
-
function DeliveryAreaPicker({ provider, onAskAssistant }) {
|
|
4717
|
-
const draft2 = useDeliveryDraft();
|
|
4718
|
-
const covered = draft2.rules.filter((r) => r.outcome !== "refuse");
|
|
4719
|
-
const coveredCities = covered.filter((r) => r.matchKind === "city");
|
|
4720
|
-
const exceptions = draft2.rules.filter((r) => r.outcome === "refuse");
|
|
4721
|
-
const cityCodes = covered.map((r) => r.cityCode).filter(Boolean);
|
|
4722
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card px-5 py-4 shadow-sm", children: [
|
|
4723
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
|
|
4724
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
4725
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Onde voc\xEA entrega?" }),
|
|
4726
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: 'Digite uma cidade, um bairro ou uma regi\xE3o \u2014 "Rio de Janeiro", "Ipanema", "Zona Sul". Se voc\xEA atende a cidade toda, uma linha s\xF3 resolve: escolha a cidade.' })
|
|
4727
|
-
] }),
|
|
4728
|
-
onAskAssistant && /* @__PURE__ */ jsxs(
|
|
4729
|
-
Button,
|
|
4730
|
-
{
|
|
4731
|
-
variant: "bare",
|
|
4732
|
-
size: "bare",
|
|
4733
|
-
type: "button",
|
|
4734
|
-
onClick: onAskAssistant,
|
|
4735
|
-
className: "inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs font-medium hover:bg-muted",
|
|
4736
|
-
children: [
|
|
4737
|
-
/* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 text-primary" }),
|
|
4738
|
-
"Prefiro contar por escrito"
|
|
4739
|
-
]
|
|
4740
|
-
}
|
|
4741
|
-
)
|
|
4742
|
-
] }),
|
|
4743
|
-
/* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
|
|
4744
|
-
AreaSearch,
|
|
4745
|
-
{
|
|
4746
|
-
provider,
|
|
4747
|
-
outcome: "deliver",
|
|
4748
|
-
placeholder: "Cidade, bairro ou regi\xE3o\u2026",
|
|
4749
|
-
emptyHint: "Nada com esse nome. Tente a cidade, ou conte por escrito ao assistente."
|
|
4750
|
-
}
|
|
4751
|
-
) }),
|
|
4752
|
-
covered.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-5 border-t border-border pt-4", children: [
|
|
4753
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4754
|
-
/* @__PURE__ */ jsx(Ban, { className: "h-4 w-4 text-destructive" }),
|
|
4755
|
-
/* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: "E onde voc\xEA n\xE3o entrega?" })
|
|
4756
|
-
] }),
|
|
4757
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: coveredCities.length > 0 ? `Recortes dentro do que voc\xEA j\xE1 cobre \u2014 comunidade, bairro de risco, regi\xE3o distante. A exce\xE7\xE3o vence a \xE1rea maior, ent\xE3o ${coveredCities[0].label} continua valendo em todo o resto.` : "Regi\xF5es que voc\xEA quer recusar mesmo estando perto do que j\xE1 cobre. A exce\xE7\xE3o vence a \xE1rea maior." }),
|
|
4758
|
-
/* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
|
|
4759
|
-
AreaSearch,
|
|
4760
|
-
{
|
|
4761
|
-
provider,
|
|
4762
|
-
outcome: "refuse",
|
|
4763
|
-
placeholder: "Bairro ou regi\xE3o que voc\xEA recusa\u2026",
|
|
4764
|
-
onlyCityCodes: cityCodes,
|
|
4765
|
-
emptyHint: /* @__PURE__ */ jsx(Fragment, { children: "Nada com esse nome. Comunidade normalmente n\xE3o tem CEP pr\xF3prio \u2014 divide o do bairro vizinho. Escolha o bairro que a cont\xE9m e marque como \u201Cconfirmo antes\u201D na tabela." })
|
|
4766
|
-
}
|
|
4767
|
-
) })
|
|
4768
|
-
] }),
|
|
4769
|
-
draft2.rules.length > 0 && /* @__PURE__ */ jsxs("p", { className: "mt-3 text-xs text-muted-foreground", children: [
|
|
4770
|
-
covered.length,
|
|
4771
|
-
" ",
|
|
4772
|
-
covered.length === 1 ? "regi\xE3o" : "regi\xF5es",
|
|
4773
|
-
" onde voc\xEA entrega",
|
|
4774
|
-
exceptions.length > 0 && `, ${exceptions.length} ${exceptions.length === 1 ? "exce\xE7\xE3o" : "exce\xE7\xF5es"}`,
|
|
4775
|
-
" ",
|
|
4776
|
-
"\u2014 confira o pre\xE7o na tabela abaixo e salve."
|
|
4777
|
-
] })
|
|
4778
|
-
] });
|
|
4779
|
-
}
|
|
4780
|
-
var OUTCOME_LABEL = {
|
|
4781
|
-
deliver: "Entrego",
|
|
4782
|
-
review: "Confirmo antes",
|
|
4783
|
-
refuse: "N\xE3o entrego"
|
|
4784
|
-
};
|
|
4785
|
-
var OUTCOME_TONE = {
|
|
4786
|
-
deliver: "text-foreground",
|
|
4787
|
-
review: "text-amber-600 dark:text-amber-500",
|
|
4788
|
-
refuse: "text-destructive"
|
|
4789
|
-
};
|
|
4790
|
-
function parseMoney(raw) {
|
|
4791
|
-
const cleaned = raw.replace(/[^\d,.-]/g, "").replace(",", ".");
|
|
4792
|
-
if (!cleaned.trim()) return null;
|
|
4793
|
-
const n = Number(cleaned);
|
|
4794
|
-
return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) / 100 : null;
|
|
4795
|
-
}
|
|
4796
|
-
var money2 = (v) => v == null ? "" : String(v).replace(".", ",");
|
|
4797
|
-
var BULK_INPUT = "h-auto rounded-md border-border bg-background px-2 py-1 text-xs shadow-none";
|
|
4798
|
-
var FIELD_FRAME = "inline-flex items-center rounded-md border border-border bg-background focus-within:ring-1 focus-within:ring-ring";
|
|
4799
|
-
var FRAMED_INPUT = "h-auto rounded-none border-0 bg-transparent py-1.5 text-xs shadow-none focus-visible:ring-0 focus-visible:ring-offset-0";
|
|
4800
|
-
function ruleArea(rule) {
|
|
4801
|
-
if (rule.matchKind === "postal_range") {
|
|
4802
|
-
const fmt = (c) => c ? `${c.slice(0, 5)}-${c.slice(5)}` : "?";
|
|
4803
|
-
return `${fmt(rule.postalFrom)} \u2192 ${fmt(rule.postalTo)}`;
|
|
4804
|
-
}
|
|
4805
|
-
return rule.matchKind === "city" ? "Munic\xEDpio" : "Bairro";
|
|
4806
|
-
}
|
|
4807
|
-
function DeliveryRulesDraft({ provider, onSaved }) {
|
|
4808
|
-
const draft2 = useDeliveryDraft();
|
|
4809
|
-
const [saving, setSaving] = React14__default.useState(false);
|
|
4810
|
-
const [error, setError] = React14__default.useState(null);
|
|
4811
|
-
const [bulkRate, setBulkRate] = React14__default.useState("");
|
|
4812
|
-
const [bulkFree, setBulkFree] = React14__default.useState("");
|
|
4813
|
-
if (draft2.rules.length === 0 && draft2.gaps.length === 0) return null;
|
|
4814
|
-
const refusing = draft2.rules.filter((r) => r.outcome === "refuse").length;
|
|
4815
|
-
const reviewing = draft2.rules.filter((r) => r.outcome === "review").length;
|
|
4816
|
-
const delivering = draft2.rules.length - refusing - reviewing;
|
|
4817
|
-
const columns = [
|
|
4818
|
-
{
|
|
4819
|
-
id: "region",
|
|
4820
|
-
header: "Regi\xE3o",
|
|
4821
|
-
meta: { className: "px-5 py-2.5" },
|
|
4822
|
-
cell: ({ row: { original: rule } }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4823
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children: rule.label }),
|
|
4824
|
-
/* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: ruleArea(rule) }),
|
|
4825
|
-
rule.source && /* @__PURE__ */ jsxs("p", { className: "text-xs italic text-muted-foreground", children: [
|
|
4826
|
-
"\u201C",
|
|
4827
|
-
rule.source,
|
|
4828
|
-
"\u201D"
|
|
4829
|
-
] })
|
|
4830
|
-
] })
|
|
4831
|
-
},
|
|
4832
|
-
{
|
|
4833
|
-
id: "outcome",
|
|
4834
|
-
header: "O que fa\xE7o",
|
|
4835
|
-
meta: { className: "px-2 py-2.5" },
|
|
4836
|
-
cell: ({ row: { original: rule } }) => /* @__PURE__ */ jsxs(
|
|
4837
|
-
Select,
|
|
4838
|
-
{
|
|
4839
|
-
value: rule.outcome,
|
|
4840
|
-
onValueChange: (v) => updateDeliveryDraftRule(rule.id, { outcome: v }),
|
|
4841
|
-
children: [
|
|
4842
|
-
/* @__PURE__ */ jsx(SelectTrigger, { className: `h-auto w-auto gap-1.5 rounded-md border-border bg-background px-2 py-1.5 text-xs shadow-none ${OUTCOME_TONE[rule.outcome]}`, children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
4843
|
-
/* @__PURE__ */ jsx(SelectContent, { children: Object.keys(OUTCOME_LABEL).map((value) => /* @__PURE__ */ jsx(SelectItem, { value, children: OUTCOME_LABEL[value] }, value)) })
|
|
4844
|
-
]
|
|
4845
|
-
}
|
|
4846
|
-
)
|
|
4847
|
-
},
|
|
4848
|
-
// Recusa não cobra: mostrar campo de preço numa linha que nunca o lê
|
|
4849
|
-
// convida a lojista a preencher e a achar que cobrou.
|
|
4850
|
-
{
|
|
4851
|
-
id: "rate",
|
|
4852
|
-
header: "Frete",
|
|
4853
|
-
meta: { className: "px-2 py-2.5" },
|
|
4854
|
-
cell: ({ row: { original: rule } }) => rule.outcome === "refuse" ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "n\xE3o se cobra" }) : /* @__PURE__ */ jsxs("span", { className: FIELD_FRAME, children: [
|
|
4855
|
-
/* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-muted-foreground", children: "R$" }),
|
|
4856
|
-
/* @__PURE__ */ jsx("div", { className: "w-16", children: /* @__PURE__ */ jsx(
|
|
4857
|
-
Input,
|
|
4858
|
-
{
|
|
4859
|
-
value: money2(rule.rate),
|
|
4860
|
-
placeholder: "0,00",
|
|
4861
|
-
"aria-label": `Frete para ${rule.label}`,
|
|
4862
|
-
onChange: (e) => updateDeliveryDraftRule(rule.id, { rate: parseMoney(e.target.value) }),
|
|
4863
|
-
className: `${FRAMED_INPUT} px-1.5`
|
|
4864
|
-
}
|
|
4865
|
-
) })
|
|
4866
|
-
] })
|
|
4867
|
-
},
|
|
4868
|
-
{
|
|
4869
|
-
id: "freeAbove",
|
|
4870
|
-
header: "Gr\xE1tis acima de",
|
|
4871
|
-
meta: { className: "px-2 py-2.5" },
|
|
4872
|
-
cell: ({ row: { original: rule } }) => rule.outcome === "refuse" ? null : /* @__PURE__ */ jsxs("span", { className: FIELD_FRAME, children: [
|
|
4873
|
-
/* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-muted-foreground", children: "R$" }),
|
|
4874
|
-
/* @__PURE__ */ jsx("div", { className: "w-20", children: /* @__PURE__ */ jsx(
|
|
4875
|
-
Input,
|
|
4876
|
-
{
|
|
4877
|
-
value: money2(rule.freeAbove),
|
|
4878
|
-
placeholder: "nunca",
|
|
4879
|
-
"aria-label": `Frete gr\xE1tis acima de, em ${rule.label}`,
|
|
4880
|
-
onChange: (e) => updateDeliveryDraftRule(rule.id, { freeAbove: parseMoney(e.target.value) }),
|
|
4881
|
-
className: `${FRAMED_INPUT} px-1.5`
|
|
4882
|
-
}
|
|
4883
|
-
) })
|
|
4884
|
-
] })
|
|
4885
|
-
},
|
|
4886
|
-
{
|
|
4887
|
-
id: "eta",
|
|
4888
|
-
header: "Prazo (dias)",
|
|
4889
|
-
meta: { className: "px-2 py-2.5" },
|
|
4890
|
-
cell: ({ row: { original: rule } }) => rule.outcome === "refuse" ? null : /* @__PURE__ */ jsxs("span", { className: FIELD_FRAME, children: [
|
|
4891
|
-
/* @__PURE__ */ jsx("div", { className: "w-10", children: /* @__PURE__ */ jsx(
|
|
4892
|
-
Input,
|
|
4893
|
-
{
|
|
4894
|
-
value: rule.etaMinDays ?? "",
|
|
4895
|
-
placeholder: "0",
|
|
4896
|
-
inputMode: "numeric",
|
|
4897
|
-
"aria-label": `Prazo m\xEDnimo em ${rule.label}`,
|
|
4898
|
-
onChange: (e) => updateDeliveryDraftRule(rule.id, {
|
|
4899
|
-
etaMinDays: e.target.value ? Number(e.target.value) : null
|
|
4900
|
-
}),
|
|
4901
|
-
className: `${FRAMED_INPUT} pl-2 pr-0 text-center`
|
|
4902
|
-
}
|
|
4903
|
-
) }),
|
|
4904
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "a" }),
|
|
4905
|
-
/* @__PURE__ */ jsx("div", { className: "w-10", children: /* @__PURE__ */ jsx(
|
|
4906
|
-
Input,
|
|
4907
|
-
{
|
|
4908
|
-
value: rule.etaMaxDays ?? "",
|
|
4909
|
-
placeholder: "2",
|
|
4910
|
-
inputMode: "numeric",
|
|
4911
|
-
"aria-label": `Prazo m\xE1ximo em ${rule.label}`,
|
|
4912
|
-
onChange: (e) => updateDeliveryDraftRule(rule.id, {
|
|
4913
|
-
etaMaxDays: e.target.value ? Number(e.target.value) : null
|
|
4914
|
-
}),
|
|
4915
|
-
className: `${FRAMED_INPUT} pl-0 pr-2 text-center`
|
|
4916
|
-
}
|
|
4917
|
-
) })
|
|
4918
|
-
] })
|
|
4919
|
-
},
|
|
4920
|
-
{
|
|
4921
|
-
id: "remove",
|
|
4922
|
-
header: "",
|
|
4923
|
-
meta: { className: "px-5 py-2.5 text-right" },
|
|
4924
|
-
cell: ({ row: { original: rule } }) => /* @__PURE__ */ jsx(
|
|
4925
|
-
Button,
|
|
4926
|
-
{
|
|
4927
|
-
variant: "bare",
|
|
4928
|
-
size: "bare",
|
|
4929
|
-
type: "button",
|
|
4930
|
-
onClick: () => removeDeliveryDraftRule(rule.id),
|
|
4931
|
-
"aria-label": `Remover ${rule.label}`,
|
|
4932
|
-
className: "rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-destructive",
|
|
4933
|
-
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
4934
|
-
}
|
|
4935
|
-
)
|
|
4936
|
-
}
|
|
4937
|
-
];
|
|
4938
|
-
const save = async () => {
|
|
4939
|
-
setSaving(true);
|
|
4940
|
-
setError(null);
|
|
4941
|
-
try {
|
|
4942
|
-
const shop = provider ? resolve(provider) : getShopProvider();
|
|
4943
|
-
if (typeof shop.createShippingZone !== "function") {
|
|
4944
|
-
throw new Error("Este banco ainda n\xE3o tem as regras de entrega.");
|
|
4945
|
-
}
|
|
4946
|
-
for (const rule of draft2.rules) {
|
|
4947
|
-
await shop.createShippingZone(zoneInputFrom(rule));
|
|
4948
|
-
}
|
|
4949
|
-
clearDeliveryDraft();
|
|
4950
|
-
onSaved();
|
|
4951
|
-
} catch (err) {
|
|
4952
|
-
setError(errorMessage(err));
|
|
4953
|
-
} finally {
|
|
4954
|
-
setSaving(false);
|
|
4955
|
-
}
|
|
4956
|
-
};
|
|
4957
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-primary/40 bg-primary/5 shadow-sm", children: [
|
|
4958
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 border-b border-primary/20 px-5 py-4", children: [
|
|
4959
|
-
/* @__PURE__ */ jsx(ListChecks, { className: "h-4 w-4 text-primary" }),
|
|
4960
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
4961
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Regras a salvar" }),
|
|
4962
|
-
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
4963
|
-
delivering,
|
|
4964
|
-
" entrega",
|
|
4965
|
-
delivering === 1 ? "" : "s",
|
|
4966
|
-
reviewing > 0 && `, ${reviewing} a confirmar`,
|
|
4967
|
-
refusing > 0 && `, ${refusing} recusa${refusing === 1 ? "" : "s"}`,
|
|
4968
|
-
" ",
|
|
4969
|
-
"\u2014 confira e corrija antes de salvar. Nada foi gravado ainda."
|
|
4970
|
-
] })
|
|
4971
|
-
] }),
|
|
4972
|
-
/* @__PURE__ */ jsx(
|
|
4973
|
-
Button,
|
|
4974
|
-
{
|
|
4975
|
-
variant: "bare",
|
|
4976
|
-
size: "bare",
|
|
4977
|
-
type: "button",
|
|
4978
|
-
onClick: clearDeliveryDraft,
|
|
4979
|
-
disabled: saving,
|
|
4980
|
-
className: "font-normal rounded-md px-3 py-2 text-xs text-muted-foreground hover:bg-muted",
|
|
4981
|
-
children: "Descartar"
|
|
4982
|
-
}
|
|
4983
|
-
),
|
|
4984
|
-
/* @__PURE__ */ jsxs(
|
|
4985
|
-
Button,
|
|
4986
|
-
{
|
|
4987
|
-
variant: "bare",
|
|
4988
|
-
size: "bare",
|
|
4989
|
-
type: "button",
|
|
4990
|
-
onClick: save,
|
|
4991
|
-
disabled: saving || draft2.rules.length === 0,
|
|
4992
|
-
className: "inline-flex items-center gap-1.5 rounded-md bg-primary px-3.5 py-2 text-xs font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
|
|
4993
|
-
children: [
|
|
4994
|
-
saving ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }),
|
|
4995
|
-
"Salvar ",
|
|
4996
|
-
draft2.rules.length,
|
|
4997
|
-
" regra",
|
|
4998
|
-
draft2.rules.length === 1 ? "" : "s"
|
|
4999
|
-
]
|
|
5000
|
-
}
|
|
5001
|
-
)
|
|
5002
|
-
] }),
|
|
5003
|
-
error && /* @__PURE__ */ jsx("p", { className: "px-5 pt-3 text-xs text-destructive", children: error }),
|
|
5004
|
-
delivering + reviewing > 1 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b border-primary/10 px-5 py-3", children: [
|
|
5005
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Mesmo pre\xE7o em todas:" }),
|
|
5006
|
-
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
5007
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "R$" }),
|
|
5008
|
-
/* @__PURE__ */ jsx("div", { className: "w-20", children: /* @__PURE__ */ jsx(
|
|
5009
|
-
Input,
|
|
5010
|
-
{
|
|
5011
|
-
value: bulkRate,
|
|
5012
|
-
onChange: (e) => setBulkRate(e.target.value),
|
|
5013
|
-
placeholder: "15,00",
|
|
5014
|
-
"aria-label": "Frete para todas as regi\xF5es",
|
|
5015
|
-
className: BULK_INPUT
|
|
5016
|
-
}
|
|
5017
|
-
) })
|
|
5018
|
-
] }),
|
|
5019
|
-
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
5020
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "gr\xE1tis acima de R$" }),
|
|
5021
|
-
/* @__PURE__ */ jsx("div", { className: "w-24", children: /* @__PURE__ */ jsx(
|
|
5022
|
-
Input,
|
|
5023
|
-
{
|
|
5024
|
-
value: bulkFree,
|
|
5025
|
-
onChange: (e) => setBulkFree(e.target.value),
|
|
5026
|
-
placeholder: "opcional",
|
|
5027
|
-
"aria-label": "Frete gr\xE1tis acima de, para todas as regi\xF5es",
|
|
5028
|
-
className: BULK_INPUT
|
|
5029
|
-
}
|
|
5030
|
-
) })
|
|
5031
|
-
] }),
|
|
5032
|
-
/* @__PURE__ */ jsx(
|
|
5033
|
-
Button,
|
|
5034
|
-
{
|
|
5035
|
-
variant: "bare",
|
|
5036
|
-
size: "bare",
|
|
5037
|
-
type: "button",
|
|
5038
|
-
onClick: () => {
|
|
5039
|
-
setAllDeliveryDraftRates({
|
|
5040
|
-
rate: parseMoney(bulkRate),
|
|
5041
|
-
freeAbove: parseMoney(bulkFree)
|
|
5042
|
-
});
|
|
5043
|
-
setBulkRate("");
|
|
5044
|
-
setBulkFree("");
|
|
5045
|
-
},
|
|
5046
|
-
disabled: !bulkRate.trim() && !bulkFree.trim(),
|
|
5047
|
-
className: "rounded-md border border-border px-2.5 py-1 text-xs font-medium hover:bg-muted disabled:opacity-40",
|
|
5048
|
-
children: "Aplicar"
|
|
5049
|
-
}
|
|
5050
|
-
)
|
|
5051
|
-
] }),
|
|
5052
|
-
draft2.rules.length > 0 && /* @__PURE__ */ jsx(DataTable, { variant: "bare", columns, data: draft2.rules }),
|
|
5053
|
-
draft2.gaps.length > 0 && /* @__PURE__ */ jsxs("div", { className: "border-t border-primary/20 px-5 py-3.5", children: [
|
|
5054
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "O que ficou de fora \u2014 e por qu\xEA" }),
|
|
5055
|
-
/* @__PURE__ */ jsx("ul", { className: "mt-1 space-y-1", children: draft2.gaps.map((gap) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2 text-xs text-muted-foreground", children: [
|
|
5056
|
-
/* @__PURE__ */ jsx(X, { className: "mt-0.5 h-3 w-3 shrink-0 text-destructive" }),
|
|
5057
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
5058
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: gap.query }),
|
|
5059
|
-
" \u2014 ",
|
|
5060
|
-
gap.reason
|
|
5061
|
-
] })
|
|
5062
|
-
] }, gap.query)) })
|
|
5063
|
-
] })
|
|
5064
|
-
] });
|
|
5065
|
-
}
|
|
5066
|
-
function parseMoney2(raw) {
|
|
5067
|
-
const cleaned = raw.replace(/[^\d,.-]/g, "").replace(",", ".");
|
|
5068
|
-
if (!cleaned.trim()) return null;
|
|
5069
|
-
const n = Number(cleaned);
|
|
5070
|
-
return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) / 100 : null;
|
|
5071
|
-
}
|
|
5072
|
-
var formatMoney = (v) => v == null ? "" : String(v).replace(".", ",");
|
|
5073
|
-
var FREIGHT_SOURCE = [
|
|
5074
|
-
{
|
|
5075
|
-
value: "own",
|
|
5076
|
-
label: "Eu mesmo calculo e despacho",
|
|
5077
|
-
description: "O frete sai do pre\xE7o base e das suas faixas de CEP, e voc\xEA despacha como quiser \u2014 carro pr\xF3prio, motoboy, ag\xEAncia. Nenhum gateway \xE9 consultado, mesmo que exista um conectado.",
|
|
5078
|
-
Icon: Car
|
|
5079
|
-
},
|
|
5080
|
-
{
|
|
5081
|
-
value: "gateway",
|
|
5082
|
-
label: "Perguntar a um gateway de frete",
|
|
5083
|
-
description: "O pre\xE7o e o prazo v\xEAm da transportadora, calculados pelo CEP do comprador. Quando o gateway n\xE3o responde a tempo, o checkout usa suas faixas \u2014 a compra nunca fica sem pre\xE7o.",
|
|
5084
|
-
Icon: Truck
|
|
5085
|
-
}
|
|
5086
|
-
];
|
|
5087
|
-
function ShippingSettings({ provider, variant = "full" }) {
|
|
5088
|
-
const embedded = variant === "delivery";
|
|
5089
|
-
const [registryId, setRegistryId] = React14__default.useState(shippingRegistryIdFromHash);
|
|
5090
|
-
React14__default.useEffect(() => {
|
|
5091
|
-
const handler = () => setRegistryId(shippingRegistryIdFromHash());
|
|
5092
|
-
window.addEventListener("hashchange", handler);
|
|
5093
|
-
return () => window.removeEventListener("hashchange", handler);
|
|
5094
|
-
}, []);
|
|
5095
|
-
const shop = React14__default.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
|
|
5096
|
-
const registries = React14__default.useMemo(
|
|
5097
|
-
() => buildShopRegistries(provider ?? getShopProvider),
|
|
5098
|
-
[provider]
|
|
5099
|
-
);
|
|
5100
|
-
const storeSettings = useStoreSettings(provider);
|
|
5101
|
-
const supported = typeof shop.getShippingSettings === "function" && typeof shop.updateShippingSettings === "function";
|
|
5102
|
-
const [row, setRow] = React14__default.useState(null);
|
|
5103
|
-
const [zones, setZones] = React14__default.useState(null);
|
|
5104
|
-
const [locations, setLocations] = React14__default.useState(null);
|
|
5105
|
-
const [loading, setLoading] = React14__default.useState(supported);
|
|
5106
|
-
const [saving, setSaving] = React14__default.useState(false);
|
|
5107
|
-
const [error, setError] = React14__default.useState(null);
|
|
5108
|
-
const [loadError, setLoadError] = React14__default.useState(null);
|
|
5109
|
-
const [flatDraft, setFlatDraft] = React14__default.useState("");
|
|
5110
|
-
const [freeDraft, setFreeDraft] = React14__default.useState("");
|
|
5111
|
-
const [flatBad, setFlatBad] = React14__default.useState(false);
|
|
5112
|
-
const [freeBad, setFreeBad] = React14__default.useState(false);
|
|
5113
|
-
const { connectors } = useConnectors({ hostPluginId: "shop", includeGrants: false });
|
|
5114
|
-
const [selected, setSelected] = React14__default.useState(null);
|
|
5115
|
-
const panelAvailable = useConnectorsPanelAvailable();
|
|
5116
|
-
const openJourney = React14__default.useCallback(
|
|
5117
|
-
(connector) => {
|
|
5118
|
-
if (panelAvailable) openConnectorsPanel(connector);
|
|
5119
|
-
else setSelected(connector);
|
|
5120
|
-
},
|
|
5121
|
-
[panelAvailable]
|
|
5122
|
-
);
|
|
5123
|
-
const carriers = React14__default.useMemo(
|
|
5124
|
-
() => (connectors ?? []).filter((c) => c.category === "logistics" || c.category === "shipping"),
|
|
5125
|
-
[connectors]
|
|
5126
|
-
);
|
|
5127
|
-
const carriersConnected = carriers.some((c) => c.state !== "ready");
|
|
5128
|
-
const [zonesEpoch, setZonesEpoch] = React14__default.useState(0);
|
|
5129
|
-
const reloadZones = React14__default.useCallback(() => setZonesEpoch((n) => n + 1), []);
|
|
5130
|
-
React14__default.useEffect(() => {
|
|
5131
|
-
let cancelled = false;
|
|
5132
|
-
if (supported) {
|
|
5133
|
-
setLoading(true);
|
|
5134
|
-
void shop.getShippingSettings().then((settings) => {
|
|
5135
|
-
if (cancelled) return;
|
|
5136
|
-
setRow(settings);
|
|
5137
|
-
setFlatDraft(formatMoney(settings?.flatRate ?? 0));
|
|
5138
|
-
setFreeDraft(formatMoney(settings?.freeAbove));
|
|
5139
|
-
}).catch((e) => {
|
|
5140
|
-
if (!cancelled) setLoadError(errorMessage(e));
|
|
5141
|
-
}).finally(() => {
|
|
5142
|
-
if (!cancelled) setLoading(false);
|
|
5143
|
-
});
|
|
5144
|
-
}
|
|
5145
|
-
void shop.listShippingZones?.().then((list) => {
|
|
5146
|
-
if (!cancelled) setZones(list);
|
|
5147
|
-
}).catch(() => {
|
|
5148
|
-
if (!cancelled) setZones([]);
|
|
5149
|
-
});
|
|
5150
|
-
void shop.listStockLocations?.().then((list) => {
|
|
5151
|
-
if (!cancelled) setLocations(list);
|
|
5152
|
-
}).catch(() => {
|
|
5153
|
-
if (!cancelled) setLocations([]);
|
|
5154
|
-
});
|
|
5155
|
-
return () => {
|
|
5156
|
-
cancelled = true;
|
|
5157
|
-
};
|
|
5158
|
-
}, [shop, supported, zonesEpoch]);
|
|
5159
|
-
const save = React14__default.useCallback(
|
|
5160
|
-
(patch) => {
|
|
5161
|
-
if (!supported) return;
|
|
5162
|
-
setSaving(true);
|
|
5163
|
-
setError(null);
|
|
5164
|
-
void shop.updateShippingSettings(patch).then((saved) => setRow(saved)).catch((e) => setError(errorMessage(e))).finally(() => setSaving(false));
|
|
5165
|
-
},
|
|
5166
|
-
[shop, supported]
|
|
5167
|
-
);
|
|
5168
|
-
const commitFlat = () => {
|
|
5169
|
-
const parsed = parseMoney2(flatDraft);
|
|
5170
|
-
const next = flatDraft.trim() === "" ? 0 : parsed;
|
|
5171
|
-
if (next == null) {
|
|
5172
|
-
setFlatBad(true);
|
|
5173
|
-
return;
|
|
5174
|
-
}
|
|
5175
|
-
setFlatBad(false);
|
|
5176
|
-
setFlatDraft(formatMoney(next));
|
|
5177
|
-
if (next !== (row?.flatRate ?? 0)) save({ flatRate: next });
|
|
5178
|
-
};
|
|
5179
|
-
const commitFree = () => {
|
|
5180
|
-
if (freeDraft.trim() === "") {
|
|
5181
|
-
setFreeBad(false);
|
|
5182
|
-
if (row?.freeAbove != null) save({ freeAbove: null });
|
|
5183
|
-
return;
|
|
5184
|
-
}
|
|
5185
|
-
const parsed = parseMoney2(freeDraft);
|
|
5186
|
-
if (parsed == null) {
|
|
5187
|
-
setFreeBad(true);
|
|
5188
|
-
return;
|
|
5189
|
-
}
|
|
5190
|
-
setFreeBad(false);
|
|
5191
|
-
setFreeDraft(formatMoney(parsed));
|
|
5192
|
-
if (parsed !== row?.freeAbove) save({ freeAbove: parsed });
|
|
5193
|
-
};
|
|
5194
|
-
const outcomeOf2 = (z) => z.outcome ?? (z.excluded ? "refuse" : "deliver");
|
|
5195
|
-
const activeZones = (zones ?? []).filter((z) => z.active && outcomeOf2(z) === "deliver");
|
|
5196
|
-
const reviewZones = (zones ?? []).filter((z) => z.active && outcomeOf2(z) === "review");
|
|
5197
|
-
const excludedZones = (zones ?? []).filter((z) => z.active && outcomeOf2(z) === "refuse");
|
|
5198
|
-
const usesGateway = storeSettings.value.carrierQuotes !== false;
|
|
5199
|
-
const dispatching = (locations ?? []).filter((l) => l.status === "active" && l.shipsOnline !== false);
|
|
5200
|
-
const originCep = dispatching.find((l) => l.postalCode)?.postalCode ?? null;
|
|
5201
|
-
const activeRegistry = registryId ? registries.find((candidate) => candidate.id === registryId) ?? null : null;
|
|
5202
|
-
if (activeRegistry) {
|
|
5203
|
-
return /* @__PURE__ */ jsx(
|
|
5204
|
-
RegistryCrudView,
|
|
5205
|
-
{
|
|
5206
|
-
registry: activeRegistry,
|
|
5207
|
-
basePath: shippingRegistryPath(activeRegistry.id)
|
|
5208
|
-
}
|
|
5209
|
-
);
|
|
5210
|
-
}
|
|
5211
|
-
if (!embedded && storeSettings.value.deliveryFlow) {
|
|
5212
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-5 text-sm shadow-sm", "data-testid": "shipping-is-delivery", children: [
|
|
5213
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium", children: "Esta loja \xE9 um delivery" }),
|
|
5214
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground", children: "Taxa de entrega, frete gr\xE1tis, regi\xF5es, pedido m\xEDnimo e tempo estimado ficam em Configura\xE7\xF5es \u203A Delivery. Esta tela \xE9 das lojas que despacham por transportadora." }),
|
|
5215
|
-
/* @__PURE__ */ jsxs(Button, { className: "mt-3", size: "sm", onClick: () => adminNavigateTo("/settings/orders-delivery"), children: [
|
|
5216
|
-
"Abrir Delivery ",
|
|
5217
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "ml-1 h-3.5 w-3.5" })
|
|
5218
|
-
] })
|
|
5219
|
-
] });
|
|
5220
|
-
}
|
|
5221
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
5222
|
-
loadError && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm", children: [
|
|
5223
|
-
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 font-medium text-destructive", children: [
|
|
5224
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4 shrink-0" }),
|
|
5225
|
-
"N\xE3o foi poss\xEDvel carregar o pre\xE7o base de entrega"
|
|
5226
|
-
] }),
|
|
5227
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground", children: "O valor abaixo \xE9 o padr\xE3o, n\xE3o o da sua loja. Recarregue antes de alter\xE1-lo \u2014 salvar agora sobrescreveria um pre\xE7o que n\xE3o conseguimos ler." }),
|
|
5228
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: loadError })
|
|
5229
|
-
] }),
|
|
5230
|
-
!supported ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-4 text-sm", children: [
|
|
5231
|
-
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 font-medium text-amber-700 dark:text-amber-500", children: [
|
|
5232
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4 shrink-0" }),
|
|
5233
|
-
"Este app n\xE3o guarda o pre\xE7o base de entrega"
|
|
5234
|
-
] }),
|
|
5235
|
-
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-muted-foreground", children: [
|
|
5236
|
-
"O provedor de loja montado aqui n\xE3o implementa ",
|
|
5237
|
-
/* @__PURE__ */ jsx("code", { children: "getShippingSettings" }),
|
|
5238
|
-
"."
|
|
5239
|
-
] })
|
|
5240
|
-
] }) : /* @__PURE__ */ jsxs(
|
|
5241
|
-
SettingsGroup,
|
|
5242
|
-
{
|
|
5243
|
-
title: embedded ? "Quanto custa a entrega" : "Pre\xE7o base",
|
|
5244
|
-
description: embedded ? "O que o site cobra para entregar quando nenhuma regi\xE3o abaixo tem pre\xE7o pr\xF3prio." : "O que a loja cobra quando nenhuma zona cobre o endere\xE7o. Uma zona que cobre o CEP sempre ganha deste n\xFAmero.",
|
|
5245
|
-
children: [
|
|
5246
|
-
/* @__PURE__ */ jsx(
|
|
5247
|
-
TextRow,
|
|
5248
|
-
{
|
|
5249
|
-
label: embedded ? "Taxa de entrega" : "Frete padr\xE3o",
|
|
5250
|
-
description: "Em reais. Deixe 0 para entrega gr\xE1tis.",
|
|
5251
|
-
value: flatDraft,
|
|
5252
|
-
onChange: setFlatDraft,
|
|
5253
|
-
onBlurCommit: commitFlat,
|
|
5254
|
-
placeholder: "0,00",
|
|
5255
|
-
invalid: flatBad,
|
|
5256
|
-
hint: flatBad ? "Informe um valor em reais (ex.: 19,90)." : void 0,
|
|
5257
|
-
disabled: loading
|
|
5258
|
-
}
|
|
5259
|
-
),
|
|
5260
|
-
/* @__PURE__ */ jsx(
|
|
5261
|
-
TextRow,
|
|
5262
|
-
{
|
|
5263
|
-
label: "Frete gr\xE1tis acima de",
|
|
5264
|
-
description: "Subtotal, j\xE1 com desconto, a partir do qual a entrega sai de gra\xE7a. Vazio = sem regra de frete gr\xE1tis.",
|
|
5265
|
-
value: freeDraft,
|
|
5266
|
-
onChange: setFreeDraft,
|
|
5267
|
-
onBlurCommit: commitFree,
|
|
5268
|
-
placeholder: "Sem regra",
|
|
5269
|
-
invalid: freeBad,
|
|
5270
|
-
hint: freeBad ? "Informe um valor em reais, ou deixe vazio." : void 0,
|
|
5271
|
-
disabled: loading
|
|
5272
|
-
}
|
|
5273
|
-
)
|
|
5274
|
-
]
|
|
5275
|
-
}
|
|
5276
|
-
),
|
|
5277
|
-
!embedded && /* @__PURE__ */ jsxs("div", { children: [
|
|
5278
|
-
/* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
|
|
5279
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "De onde voc\xEA despacha" }),
|
|
5280
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Os locais que guardam estoque e despacham pedidos. O CEP do local padr\xE3o \xE9 a origem que a transportadora usa para cotar o frete \u2014 \xE9 aqui que ele se cadastra, uma vez. Cada local pode ter o seu pr\xF3prio despachante ou seguir a decis\xE3o da loja." })
|
|
5281
|
-
] }),
|
|
5282
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
|
|
5283
|
-
Button,
|
|
5284
|
-
{
|
|
5285
|
-
variant: "bare",
|
|
5286
|
-
size: "bare",
|
|
5287
|
-
type: "button",
|
|
5288
|
-
onClick: () => {
|
|
5289
|
-
window.location.hash = dispatching.length === 1 && dispatching[0] ? shippingRecordPath(LOCATIONS_REGISTRY, dispatching[0].id) : shippingRegistryPath(LOCATIONS_REGISTRY);
|
|
5290
|
-
},
|
|
5291
|
-
className: "font-normal justify-start whitespace-normal flex w-full items-center gap-3 py-3.5 text-left",
|
|
5292
|
-
children: [
|
|
5293
|
-
/* @__PURE__ */ jsx(Warehouse, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
5294
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
5295
|
-
/* @__PURE__ */ jsx("span", { className: "block text-sm font-medium", children: locations == null ? "Carregando locais\u2026" : dispatching.length === 0 ? "Nenhum local despacha pedidos" : dispatching.length === 1 ? dispatching[0].name : `${dispatching.length} locais despachando` }),
|
|
5296
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: locations == null ? " " : dispatching.length === 0 ? "Cadastre o local de onde os pedidos saem." : dispatching.slice(0, 2).map((l) => locationSummary(l)).join(" \u2014 ") })
|
|
5297
|
-
] }),
|
|
5298
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4 shrink-0 text-muted-foreground" })
|
|
5299
|
-
]
|
|
5300
|
-
}
|
|
5301
|
-
) }),
|
|
5302
|
-
usesGateway && locations != null && dispatching.length > 0 && !originCep && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-start gap-2 rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs", children: [
|
|
5303
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600" }),
|
|
5304
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Nenhum local tem CEP de origem. Sem ele a transportadora n\xE3o cota, e o checkout cai nas suas zonas \u2014 preencha o CEP do local padr\xE3o." })
|
|
5305
|
-
] })
|
|
5306
|
-
] }),
|
|
5307
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
5308
|
-
/* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
|
|
5309
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Onde voc\xEA entrega" }),
|
|
5310
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "As regi\xF5es onde a loja entrega, com pre\xE7o, prazo e frete gr\xE1tis pr\xF3prios. Um endere\xE7o fora de todas elas cai no pre\xE7o base acima; se a loja n\xE3o tiver pre\xE7o base nem regi\xE3o que o cubra, o checkout diz que n\xE3o entrega ali. Uma regi\xE3o tamb\xE9m pode ser uma recusa \u2014 \xABn\xE3o entrego aqui\xBB \u2014 e ela vence qualquer outra e qualquer gateway." })
|
|
5311
|
-
] }),
|
|
5312
|
-
/* @__PURE__ */ jsx(
|
|
5313
|
-
DeliveryAgentSurface,
|
|
5314
|
-
{
|
|
5315
|
-
provider: () => shop,
|
|
5316
|
-
zones: zones ?? [],
|
|
5317
|
-
onSaved: reloadZones
|
|
5318
|
-
}
|
|
5319
|
-
),
|
|
5320
|
-
/* @__PURE__ */ jsx(DeliveryAreaPicker, { provider }),
|
|
5321
|
-
/* @__PURE__ */ jsx(DeliveryRulesDraft, { provider, onSaved: reloadZones }),
|
|
5322
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
|
|
5323
|
-
Button,
|
|
5324
|
-
{
|
|
5325
|
-
variant: "bare",
|
|
5326
|
-
size: "bare",
|
|
5327
|
-
type: "button",
|
|
5328
|
-
onClick: () => {
|
|
5329
|
-
window.location.hash = shippingRegistryPath(SHIPPING_ZONES_REGISTRY);
|
|
5330
|
-
},
|
|
5331
|
-
className: "font-normal justify-start whitespace-normal flex w-full items-center gap-3 py-3.5 text-left",
|
|
5332
|
-
children: [
|
|
5333
|
-
/* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
5334
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
5335
|
-
/* @__PURE__ */ jsx("span", { className: "block text-sm font-medium", children: zones == null ? "Carregando regi\xF5es\u2026" : activeZones.length === 0 ? "Nenhuma regi\xE3o salva ainda" : `${activeZones.length} ${activeZones.length === 1 ? "regi\xE3o onde entrego" : "regi\xF5es onde entrego"}` }),
|
|
5336
|
-
/* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: [
|
|
5337
|
-
activeZones.length === 0 ? "Sem regi\xE3o salva, toda entrega usa o pre\xE7o base. Escolha as suas na busca acima." : activeZones.slice(0, 3).map((z) => z.name).join(" \xB7 "),
|
|
5338
|
-
activeZones.length > 3 && ` e ${activeZones.length - 3} mais`,
|
|
5339
|
-
reviewZones.length > 0 && /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-amber-700 dark:text-amber-500", children: [
|
|
5340
|
-
reviewZones.length === 1 ? "1 regi\xE3o a confirmar antes de sair" : `${reviewZones.length} regi\xF5es a confirmar antes de sair`,
|
|
5341
|
-
" \u2014 ",
|
|
5342
|
-
reviewZones.slice(0, 2).map((z) => z.name).join(" \xB7 ")
|
|
5343
|
-
] }),
|
|
5344
|
-
excludedZones.length > 0 && /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-red-700 dark:text-red-400", children: [
|
|
5345
|
-
excludedZones.length === 1 ? "1 regi\xE3o recusada" : `${excludedZones.length} regi\xF5es recusadas`,
|
|
5346
|
-
" \u2014 ",
|
|
5347
|
-
excludedZones.slice(0, 2).map((z) => z.name).join(" \xB7 ")
|
|
5348
|
-
] })
|
|
5349
|
-
] })
|
|
5350
|
-
] }),
|
|
5351
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4 shrink-0 text-muted-foreground" })
|
|
5352
|
-
]
|
|
5353
|
-
}
|
|
5354
|
-
) })
|
|
5355
|
-
] }),
|
|
5356
|
-
!embedded && /* @__PURE__ */ jsx(PackagesSettings, {}),
|
|
5357
|
-
!embedded && storeSettings.supported && /* @__PURE__ */ jsxs("div", { children: [
|
|
5358
|
-
/* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
|
|
5359
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Quem calcula o frete" }),
|
|
5360
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "O Melhor Envio n\xE3o \xE9 uma transportadora: \xE9 um gateway, uma conex\xE3o s\xF3 que traz Correios, Jadlog, Loggi e outras. Ent\xE3o a escolha aqui n\xE3o \xE9 qual transportadora \u2014 \xE9 se a loja calcula pelas pr\xF3prias faixas ou se pergunta o pre\xE7o a um gateway." })
|
|
5361
|
-
] }),
|
|
5362
|
-
/* @__PURE__ */ jsx(
|
|
5363
|
-
ChoiceCards,
|
|
5364
|
-
{
|
|
5365
|
-
label: "Quem calcula o frete",
|
|
5366
|
-
choices: FREIGHT_SOURCE,
|
|
5367
|
-
value: usesGateway ? "gateway" : "own",
|
|
5368
|
-
onChange: (choice) => storeSettings.set({ carrierQuotes: choice === "gateway" }),
|
|
5369
|
-
disabled: storeSettings.loading
|
|
5370
|
-
}
|
|
5371
|
-
),
|
|
5372
|
-
!usesGateway && carriersConnected && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-start gap-2 rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs", children: [
|
|
5373
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600" }),
|
|
5374
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "H\xE1 um gateway ainda conectado abaixo. Para garantir que nenhum pre\xE7o de transportadora apare\xE7a no checkout, desconecte-o \u2014 a escolha acima vale para a vitrine, a conex\xE3o continua existindo." })
|
|
5375
|
-
] })
|
|
5376
|
-
] }),
|
|
5377
|
-
!embedded && carriers.length > 0 && /* @__PURE__ */ jsxs("div", { className: usesGateway ? void 0 : "opacity-60", children: [
|
|
5378
|
-
/* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
|
|
5379
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Gateway de frete" }),
|
|
5380
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Conectado, ele cota o frete para o CEP do comprador em vez de a loja adivinhar. O pre\xE7o cotado \xE9 gravado no servidor e \xE9 ele que o pedido cobra \u2014 o navegador escolhe entre op\xE7\xF5es, nunca informa um valor. Quais transportadoras aparecem vem da conta do gateway, n\xE3o desta lista." }),
|
|
5381
|
-
!carriersConnected && /* @__PURE__ */ jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "Nenhum conectado \u2014 a loja entrega por conta pr\xF3pria, pelo pre\xE7o base e pelas zonas acima. Conectar \xE9 opcional e pode ficar para depois." })
|
|
5382
|
-
] }),
|
|
5383
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card shadow-sm", children: /* @__PURE__ */ jsx("ul", { className: "divide-y", children: carriers.map((connector) => /* @__PURE__ */ jsx(ConnectorRow, { connector, onOpen: openJourney }, connector.id)) }) })
|
|
5384
|
-
] }),
|
|
5385
|
-
!embedded && /* @__PURE__ */ jsx(HandlingSettings, { settings: storeSettings, provider }),
|
|
5386
|
-
/* @__PURE__ */ jsx("div", { className: "flex min-h-5 items-center gap-1.5 px-1 text-xs text-muted-foreground", children: error ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-destructive", children: [
|
|
5387
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
5388
|
-
"N\xE3o foi poss\xEDvel salvar: ",
|
|
5389
|
-
error
|
|
5390
|
-
] }) : saving ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5391
|
-
/* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 shrink-0 animate-spin" }),
|
|
5392
|
-
"Salvando\u2026"
|
|
5393
|
-
] }) : "As altera\xE7\xF5es s\xE3o salvas ao sair do campo." }),
|
|
5394
|
-
/* @__PURE__ */ jsx(
|
|
5395
|
-
ConnectorDetail,
|
|
5396
|
-
{
|
|
5397
|
-
connector: selected,
|
|
5398
|
-
open: !!selected,
|
|
5399
|
-
onOpenChange: (open) => {
|
|
5400
|
-
if (!open) setSelected(null);
|
|
5401
|
-
}
|
|
5402
|
-
}
|
|
5403
|
-
)
|
|
5404
|
-
] });
|
|
5405
|
-
}
|
|
5406
|
-
function parseNumber2(raw) {
|
|
5407
|
-
const clean = raw.replace(/[^\d,.-]/g, "").replace(",", ".");
|
|
5408
|
-
if (!clean.trim()) return null;
|
|
5409
|
-
const n = Number(clean);
|
|
5410
|
-
return Number.isFinite(n) && n >= 0 ? n : NaN;
|
|
5411
|
-
}
|
|
5412
|
-
var text3 = (v) => v == null ? "" : String(v).replace(".", ",");
|
|
5413
|
-
function NumberRow({ label, description, hint, value, onCommit, placeholder, integer }) {
|
|
5414
|
-
const [draft2, setDraft] = React14__default.useState(() => text3(value));
|
|
5415
|
-
const [invalid, setInvalid] = React14__default.useState(false);
|
|
5416
|
-
React14__default.useEffect(() => {
|
|
5417
|
-
setDraft(text3(value));
|
|
5418
|
-
}, [value]);
|
|
5419
|
-
return /* @__PURE__ */ jsx(
|
|
5420
|
-
TextRow,
|
|
5421
|
-
{
|
|
5422
|
-
label,
|
|
5423
|
-
description,
|
|
5424
|
-
value: draft2,
|
|
5425
|
-
onChange: (next) => {
|
|
5426
|
-
setDraft(next);
|
|
5427
|
-
setInvalid(false);
|
|
5428
|
-
},
|
|
5429
|
-
onBlurCommit: () => {
|
|
5430
|
-
const parsed = parseNumber2(draft2);
|
|
5431
|
-
if (Number.isNaN(parsed)) {
|
|
5432
|
-
setInvalid(true);
|
|
5433
|
-
return;
|
|
5434
|
-
}
|
|
5435
|
-
setInvalid(false);
|
|
5436
|
-
onCommit(parsed == null ? null : integer ? Math.round(parsed) : Math.round(parsed * 100) / 100);
|
|
5437
|
-
},
|
|
5438
|
-
invalid,
|
|
5439
|
-
hint: invalid ? "Use um n\xFAmero, como 9,90." : hint,
|
|
5440
|
-
placeholder
|
|
5441
|
-
}
|
|
5442
|
-
);
|
|
5443
|
-
}
|
|
5444
|
-
function EtaRow({ minutes, onCommit }) {
|
|
5445
|
-
const [unit, setUnit] = React14__default.useState(() => minutes != null && minutes >= 60 && minutes % 60 === 0 ? "h" : "min");
|
|
5446
|
-
const shown = (m, u) => m == null ? "" : u === "h" ? String(+(m / 60).toFixed(2)).replace(".", ",") : String(m);
|
|
5447
|
-
const [draft2, setDraft] = React14__default.useState(() => shown(minutes, unit));
|
|
5448
|
-
const [invalid, setInvalid] = React14__default.useState(false);
|
|
5449
|
-
React14__default.useEffect(() => {
|
|
5450
|
-
setDraft(shown(minutes, unit));
|
|
5451
|
-
}, [minutes, unit]);
|
|
5452
|
-
const commit = (raw, u) => {
|
|
5453
|
-
const parsed = parseNumber2(raw);
|
|
5454
|
-
if (Number.isNaN(parsed)) {
|
|
5455
|
-
setInvalid(true);
|
|
5456
|
-
return;
|
|
5457
|
-
}
|
|
5458
|
-
setInvalid(false);
|
|
5459
|
-
onCommit(parsed == null || parsed === 0 ? null : Math.round(u === "h" ? parsed * 60 : parsed));
|
|
5460
|
-
};
|
|
5461
|
-
return /* @__PURE__ */ jsxs("div", { className: "py-3", children: [
|
|
5462
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Tempo estimado" }),
|
|
5463
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: "Da confirma\xE7\xE3o at\xE9 a porta. Aparece no topo do card\xE1pio e no aviso de pedido aceito \u2014 de 24 h em diante, como \xABat\xE9 24 h\xBB." }),
|
|
5464
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex max-w-xs items-center gap-2", children: [
|
|
5465
|
-
/* @__PURE__ */ jsx(
|
|
5466
|
-
Input,
|
|
5467
|
-
{
|
|
5468
|
-
value: draft2,
|
|
5469
|
-
placeholder: "N\xE3o informar",
|
|
5470
|
-
"aria-invalid": invalid || void 0,
|
|
5471
|
-
"aria-label": "Tempo estimado",
|
|
5472
|
-
onChange: (e) => {
|
|
5473
|
-
setDraft(e.target.value);
|
|
5474
|
-
setInvalid(false);
|
|
5475
|
-
},
|
|
5476
|
-
onBlur: () => commit(draft2, unit),
|
|
5477
|
-
onKeyDown: (e) => {
|
|
5478
|
-
if (e.key === "Enter") commit(draft2, unit);
|
|
5479
|
-
},
|
|
5480
|
-
"data-testid": "delivery-eta-value"
|
|
5481
|
-
}
|
|
5482
|
-
),
|
|
5483
|
-
/* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": "Unidade", className: "flex shrink-0 rounded-md border p-0.5 text-xs", children: ["min", "h"].map((u) => /* @__PURE__ */ jsx(
|
|
5484
|
-
"button",
|
|
5485
|
-
{
|
|
5486
|
-
type: "button",
|
|
5487
|
-
role: "radio",
|
|
5488
|
-
"aria-checked": unit === u,
|
|
5489
|
-
"data-testid": `delivery-eta-unit-${u}`,
|
|
5490
|
-
onClick: () => {
|
|
5491
|
-
if (u !== unit) {
|
|
5492
|
-
setUnit(u);
|
|
5493
|
-
setDraft(shown(minutes, u));
|
|
5494
|
-
}
|
|
5495
|
-
},
|
|
5496
|
-
className: `rounded px-2.5 py-1 ${unit === u ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"}`,
|
|
5497
|
-
children: u === "min" ? "minutos" : "horas"
|
|
5498
|
-
},
|
|
5499
|
-
u
|
|
5500
|
-
)) })
|
|
5501
|
-
] }),
|
|
5502
|
-
/* @__PURE__ */ jsx("p", { className: `mt-1 text-xs ${invalid ? "text-destructive" : "text-muted-foreground"}`, children: invalid ? "Use um n\xFAmero, como 45 ou 1,5." : "Vazio = n\xE3o mostrar." })
|
|
5503
|
-
] });
|
|
5504
|
-
}
|
|
5505
|
-
function DeliverySwitchStatus() {
|
|
5506
|
-
const unitId = useActiveUnitId();
|
|
5507
|
-
const s = useSettingKeys(["delivery.enabled"], unitId ?? null);
|
|
5508
|
-
if (!s.supported || s.loading || s.loadError) return null;
|
|
5509
|
-
const open = s.values["delivery.enabled"] !== false;
|
|
5510
|
-
return /* @__PURE__ */ jsxs("p", { "data-testid": "delivery-switch-status", className: "flex items-center gap-2 py-3 text-sm", children: [
|
|
5511
|
-
/* @__PURE__ */ jsx("span", { className: `h-2 w-2 shrink-0 rounded-full ${open ? "bg-emerald-500" : "bg-amber-500"}`, "aria-hidden": true }),
|
|
5512
|
-
open ? /* @__PURE__ */ jsxs("span", { children: [
|
|
5513
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Delivery aberto agora" }),
|
|
5514
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: " \xB7 pause pela chave Delivery no topo" })
|
|
5515
|
-
] }) : /* @__PURE__ */ jsxs("span", { children: [
|
|
5516
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Delivery pausado" }),
|
|
5517
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: " \xB7 o site mostra fechado" })
|
|
5518
|
-
] })
|
|
5519
|
-
] });
|
|
5520
|
-
}
|
|
5521
|
-
function DeliveryStoreSettings({ provider }) {
|
|
5522
|
-
const settings = useStoreSettings(provider);
|
|
5523
|
-
const { value, set } = settings;
|
|
5524
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", "data-testid": "delivery-store-settings", children: [
|
|
5525
|
-
/* @__PURE__ */ jsx(
|
|
5526
|
-
SettingsGroup,
|
|
5527
|
-
{
|
|
5528
|
-
title: "Pedidos do site",
|
|
5529
|
-
description: "O delivery do site abre e pausa pela chave Delivery no topo do sistema.",
|
|
5530
|
-
children: /* @__PURE__ */ jsx(DeliverySwitchStatus, {})
|
|
5531
|
-
}
|
|
5532
|
-
),
|
|
5533
|
-
/* @__PURE__ */ jsxs(
|
|
5534
|
-
SettingsGroup,
|
|
5535
|
-
{
|
|
5536
|
-
title: "Pedido m\xEDnimo e tempo",
|
|
5537
|
-
description: "O que o site exige e promete em cada pedido de delivery.",
|
|
5538
|
-
children: [
|
|
5539
|
-
/* @__PURE__ */ jsx(
|
|
5540
|
-
NumberRow,
|
|
5541
|
-
{
|
|
5542
|
-
label: "Pedido m\xEDnimo",
|
|
5543
|
-
description: "Valor dos itens abaixo do qual o site n\xE3o fecha o pedido.",
|
|
5544
|
-
hint: "Vazio ou zero = sem m\xEDnimo.",
|
|
5545
|
-
value: value.minOrderTotal,
|
|
5546
|
-
placeholder: "Sem m\xEDnimo",
|
|
5547
|
-
onCommit: (next) => set({ minOrderTotal: next ?? null })
|
|
5548
|
-
}
|
|
5549
|
-
),
|
|
5550
|
-
/* @__PURE__ */ jsx(EtaRow, { minutes: value.deliveryEtaMinutes, onCommit: (next) => set({ deliveryEtaMinutes: next }) })
|
|
5551
|
-
]
|
|
5552
|
-
}
|
|
5553
|
-
),
|
|
5554
|
-
settings.error && /* @__PURE__ */ jsxs("p", { className: "px-1 text-xs text-destructive", children: [
|
|
5555
|
-
"N\xE3o foi poss\xEDvel salvar: ",
|
|
5556
|
-
settings.error
|
|
5557
|
-
] }),
|
|
5558
|
-
/* @__PURE__ */ jsx(ShippingSettings, { provider, variant: "delivery" })
|
|
5559
|
-
] });
|
|
5560
|
-
}
|
|
5561
|
-
var POLICIES = [
|
|
5562
|
-
{
|
|
5563
|
-
value: "on_payment",
|
|
5564
|
-
label: "Dar baixa quando o pagamento for confirmado",
|
|
5565
|
-
description: "A remessa \xE9 criada assim que o pedido \xE9 pago \u2014 pelo gateway ou por voc\xEA aqui no admin. \xC9 a op\xE7\xE3o segura: nada sai antes de o dinheiro entrar."
|
|
5566
|
-
},
|
|
5567
|
-
{
|
|
5568
|
-
value: "always",
|
|
5569
|
-
label: "Dar baixa em todo pedido, automaticamente",
|
|
5570
|
-
description: "A remessa nasce junto com o pedido, pago ou n\xE3o. Para quem cobra na entrega ou separa antes de receber."
|
|
5571
|
-
},
|
|
5572
|
-
{
|
|
5573
|
-
value: "never",
|
|
5574
|
-
label: "N\xE3o dar baixa autom\xE1tica",
|
|
5575
|
-
description: "Cada remessa \xE9 criada \xE0 m\xE3o, na tela do pedido. \xC9 o padr\xE3o."
|
|
5576
|
-
}
|
|
5577
|
-
];
|
|
5578
|
-
function FulfillmentSettings({ provider }) {
|
|
5579
|
-
const settings = useStoreSettings(provider, { autoFulfill: "never" });
|
|
5580
|
-
const { value, set } = settings;
|
|
5581
|
-
const current = value.autoFulfill ?? "never";
|
|
5582
|
-
return /* @__PURE__ */ jsxs(SettingsState, { ...settings, children: [
|
|
5583
|
-
/* @__PURE__ */ jsx(
|
|
5584
|
-
SettingsGroup,
|
|
5585
|
-
{
|
|
5586
|
-
title: "Baixa autom\xE1tica",
|
|
5587
|
-
description: "Quando um registro de remessa \xE9 criado para um pedido novo.",
|
|
5588
|
-
children: /* @__PURE__ */ jsx(
|
|
5589
|
-
ChoiceCards,
|
|
5590
|
-
{
|
|
5591
|
-
label: "Baixa autom\xE1tica",
|
|
5592
|
-
choices: POLICIES,
|
|
5593
|
-
value: current,
|
|
5594
|
-
onChange: (autoFulfill) => set({ autoFulfill })
|
|
5595
|
-
}
|
|
5596
|
-
)
|
|
5597
|
-
}
|
|
5598
|
-
),
|
|
5599
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-lg border bg-muted/30 p-3 text-xs text-muted-foreground", children: [
|
|
5600
|
-
/* @__PURE__ */ jsx(Info, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
|
|
5601
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
5602
|
-
"A remessa criada aqui nasce ",
|
|
5603
|
-
/* @__PURE__ */ jsx("strong", { className: "font-medium", children: "pendente" }),
|
|
5604
|
-
", com os itens do pedido e sem transportadora nem c\xF3digo de rastreio \u2014 quem despacha preenche isso na tela do pedido. Pedidos que j\xE1 foram feitos n\xE3o s\xE3o afetados."
|
|
5605
|
-
] })
|
|
5606
|
-
] })
|
|
5607
|
-
] });
|
|
5608
|
-
}
|
|
5609
|
-
function MarketsSettings({ basePath = "/settings/shop-markets" }) {
|
|
5610
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
5611
|
-
/* @__PURE__ */ jsxs("div", { className: "px-1 pt-4", children: [
|
|
5612
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Mercados" }),
|
|
5613
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: marketsRegistry.description })
|
|
5614
|
-
] }),
|
|
5615
|
-
/* @__PURE__ */ jsx(RegistryCrudView, { registry: marketsRegistry, basePath, embedded: true })
|
|
5616
|
-
] });
|
|
3056
|
+
maxOrders: input.maxOrders ?? null,
|
|
3057
|
+
minSpent: input.minSpent ?? null,
|
|
3058
|
+
maxSpent: input.maxSpent ?? null,
|
|
3059
|
+
joinedAfter: input.joinedAfter || null,
|
|
3060
|
+
joinedBefore: input.joinedBefore || null
|
|
3061
|
+
});
|
|
3062
|
+
return { ...created, matchCount: 0 };
|
|
3063
|
+
},
|
|
3064
|
+
async update(id, data) {
|
|
3065
|
+
const p = requireSegments(resolve(provider));
|
|
3066
|
+
const { matchCount: _count, ...patch } = data;
|
|
3067
|
+
const updated = await p.updateCustomerSegment(id, patch);
|
|
3068
|
+
return { ...updated, matchCount: 0 };
|
|
3069
|
+
},
|
|
3070
|
+
async remove(id) {
|
|
3071
|
+
const p = requireSegments(resolve(provider));
|
|
3072
|
+
await p.deleteCustomerSegment(id);
|
|
3073
|
+
}
|
|
3074
|
+
};
|
|
5617
3075
|
}
|
|
5618
3076
|
var MELHOR_ENVIO_CONNECTOR_ID = "melhor-envio";
|
|
5619
3077
|
var MELHOR_ENVIO_LATENCY_BUDGET_MS = 2500;
|
|
@@ -6840,505 +4298,6 @@ var mercadoPagoConnectorDef = {
|
|
|
6840
4298
|
ExtraPanel: MercadoPagoExtraPanel
|
|
6841
4299
|
};
|
|
6842
4300
|
|
|
6843
|
-
// src/views/media-links.ts
|
|
6844
|
-
function recordRoute(reference) {
|
|
6845
|
-
return `#/products/${reference.id}/images`;
|
|
6846
|
-
}
|
|
6847
|
-
function downloadUrlOf(file) {
|
|
6848
|
-
if (!/^https?:/i.test(file.url)) return file.url;
|
|
6849
|
-
const separator = file.url.includes("?") ? "&" : "?";
|
|
6850
|
-
return `${file.url}${separator}download=${encodeURIComponent(file.name)}`;
|
|
6851
|
-
}
|
|
6852
|
-
var TABS = [
|
|
6853
|
-
{ value: "all", label: "Todos" },
|
|
6854
|
-
{ value: "image", label: "Imagens" },
|
|
6855
|
-
{ value: "video", label: "V\xEDdeos" }
|
|
6856
|
-
];
|
|
6857
|
-
var LOW_RESOLUTION_PX = 800;
|
|
6858
|
-
function resolve2(resolver) {
|
|
6859
|
-
return typeof resolver === "function" ? resolver() : resolver ?? getShopProvider();
|
|
6860
|
-
}
|
|
6861
|
-
function formatSize(bytes) {
|
|
6862
|
-
if (!bytes) return "";
|
|
6863
|
-
const units = ["B", "KB", "MB", "GB"];
|
|
6864
|
-
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
|
|
6865
|
-
return `${(bytes / 1024 ** i).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
|
|
6866
|
-
}
|
|
6867
|
-
function ContentPage({ resolveProvider }) {
|
|
6868
|
-
const provider = React14__default.useMemo(() => resolve2(resolveProvider), [resolveProvider]);
|
|
6869
|
-
const supported = typeof provider.listMediaFiles === "function";
|
|
6870
|
-
const [files, setFiles] = React14__default.useState(null);
|
|
6871
|
-
const [tab, setTab] = React14__default.useState("all");
|
|
6872
|
-
const [uploading, setUploading] = React14__default.useState(false);
|
|
6873
|
-
const [pendingDelete, setPendingDelete] = React14__default.useState(null);
|
|
6874
|
-
const [deleting, setDeleting] = React14__default.useState(false);
|
|
6875
|
-
const [replacing, setReplacing] = React14__default.useState(false);
|
|
6876
|
-
const [openId, setOpenId] = React14__default.useState(null);
|
|
6877
|
-
const [dimensions, setDimensions] = React14__default.useState({});
|
|
6878
|
-
const uploadInputRef = React14__default.useRef(null);
|
|
6879
|
-
const reload = React14__default.useCallback(async () => {
|
|
6880
|
-
if (!provider.listMediaFiles) {
|
|
6881
|
-
setFiles([]);
|
|
6882
|
-
return;
|
|
6883
|
-
}
|
|
6884
|
-
try {
|
|
6885
|
-
setFiles(await provider.listMediaFiles());
|
|
6886
|
-
} catch (err) {
|
|
6887
|
-
console.warn("[content] failed to list media", err);
|
|
6888
|
-
setFiles([]);
|
|
6889
|
-
}
|
|
6890
|
-
}, [provider]);
|
|
6891
|
-
React14__default.useEffect(() => {
|
|
6892
|
-
void reload();
|
|
6893
|
-
}, [reload]);
|
|
6894
|
-
const measure = React14__default.useCallback((id, d) => {
|
|
6895
|
-
setDimensions((prev) => prev[id] ? prev : { ...prev, [id]: d });
|
|
6896
|
-
}, []);
|
|
6897
|
-
const handleUpload = async (picked) => {
|
|
6898
|
-
if (!picked?.length || !provider.uploadMediaFile) return;
|
|
6899
|
-
setUploading(true);
|
|
6900
|
-
try {
|
|
6901
|
-
for (const file of Array.from(picked)) {
|
|
6902
|
-
await provider.uploadMediaFile(file);
|
|
6903
|
-
}
|
|
6904
|
-
toast.success(picked.length === 1 ? "Arquivo enviado" : `${picked.length} arquivos enviados`);
|
|
6905
|
-
await reload();
|
|
6906
|
-
} catch (err) {
|
|
6907
|
-
console.warn("[content] upload failed", err);
|
|
6908
|
-
toast.error("N\xE3o foi poss\xEDvel enviar o arquivo");
|
|
6909
|
-
} finally {
|
|
6910
|
-
setUploading(false);
|
|
6911
|
-
}
|
|
6912
|
-
};
|
|
6913
|
-
const handleReplace = async (file, picked) => {
|
|
6914
|
-
setReplacing(true);
|
|
6915
|
-
try {
|
|
6916
|
-
let nextId2;
|
|
6917
|
-
if (file.reference) {
|
|
6918
|
-
if (!provider.replaceProductImage) throw new Error("unsupported");
|
|
6919
|
-
const image = await provider.replaceProductImage(file.reference.attachmentId, picked);
|
|
6920
|
-
nextId2 = productImageToMediaFile(image, file.reference.label).id;
|
|
6921
|
-
} else {
|
|
6922
|
-
if (!provider.uploadMediaFile) throw new Error("unsupported");
|
|
6923
|
-
const uploaded = await provider.uploadMediaFile(picked);
|
|
6924
|
-
if (provider.deleteMediaFile) await provider.deleteMediaFile(file.id);
|
|
6925
|
-
nextId2 = uploaded.id;
|
|
6926
|
-
}
|
|
6927
|
-
await reload();
|
|
6928
|
-
setOpenId(nextId2);
|
|
6929
|
-
toast.success("Arquivo trocado");
|
|
6930
|
-
} catch (err) {
|
|
6931
|
-
console.warn("[content] replace failed", err);
|
|
6932
|
-
toast.error("N\xE3o foi poss\xEDvel trocar o arquivo");
|
|
6933
|
-
} finally {
|
|
6934
|
-
setReplacing(false);
|
|
6935
|
-
}
|
|
6936
|
-
};
|
|
6937
|
-
const confirmDelete = async () => {
|
|
6938
|
-
const file = pendingDelete;
|
|
6939
|
-
if (!file) return;
|
|
6940
|
-
setDeleting(true);
|
|
6941
|
-
try {
|
|
6942
|
-
if (file.reference) {
|
|
6943
|
-
await provider.deleteProductImage(file.reference.attachmentId);
|
|
6944
|
-
} else if (provider.deleteMediaFile) {
|
|
6945
|
-
await provider.deleteMediaFile(file.id);
|
|
6946
|
-
}
|
|
6947
|
-
setFiles((prev) => prev?.filter((f) => f.id !== file.id) ?? null);
|
|
6948
|
-
setPendingDelete(null);
|
|
6949
|
-
if (openId === file.id) setOpenId(null);
|
|
6950
|
-
} catch (err) {
|
|
6951
|
-
console.warn("[content] delete failed", err);
|
|
6952
|
-
toast.error("N\xE3o foi poss\xEDvel excluir o arquivo");
|
|
6953
|
-
} finally {
|
|
6954
|
-
setDeleting(false);
|
|
6955
|
-
}
|
|
6956
|
-
};
|
|
6957
|
-
const visible = (files ?? []).filter((f) => tab === "all" || f.kind === tab);
|
|
6958
|
-
const openIndex = visible.findIndex((f) => f.id === openId);
|
|
6959
|
-
const open = openIndex >= 0 ? visible[openIndex] : null;
|
|
6960
|
-
const canDelete = (file) => Boolean(file.reference) || typeof provider.deleteMediaFile === "function";
|
|
6961
|
-
const canReplace = (file) => file.reference ? typeof provider.replaceProductImage === "function" : typeof provider.uploadMediaFile === "function";
|
|
6962
|
-
if (open) {
|
|
6963
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6964
|
-
/* @__PURE__ */ jsx(
|
|
6965
|
-
MediaDetail,
|
|
6966
|
-
{
|
|
6967
|
-
file: open,
|
|
6968
|
-
dimensions: dimensions[open.id],
|
|
6969
|
-
onMeasured: (d) => measure(open.id, d),
|
|
6970
|
-
position: { index: openIndex, total: visible.length },
|
|
6971
|
-
onNavigate: (delta) => setOpenId(visible[openIndex + delta]?.id ?? open.id),
|
|
6972
|
-
onBack: () => setOpenId(null),
|
|
6973
|
-
onReplace: canReplace(open) ? (picked) => void handleReplace(open, picked) : void 0,
|
|
6974
|
-
onDelete: canDelete(open) ? () => setPendingDelete(open) : void 0,
|
|
6975
|
-
replacing
|
|
6976
|
-
}
|
|
6977
|
-
),
|
|
6978
|
-
/* @__PURE__ */ jsx(
|
|
6979
|
-
DeleteConfirm,
|
|
6980
|
-
{
|
|
6981
|
-
file: pendingDelete,
|
|
6982
|
-
loading: deleting,
|
|
6983
|
-
onConfirm: () => void confirmDelete(),
|
|
6984
|
-
onCancel: () => setPendingDelete(null)
|
|
6985
|
-
}
|
|
6986
|
-
)
|
|
6987
|
-
] });
|
|
6988
|
-
}
|
|
6989
|
-
const attached = visible.filter((f) => f.reference);
|
|
6990
|
-
const loose = visible.filter((f) => !f.reference);
|
|
6991
|
-
const countOf = (t) => {
|
|
6992
|
-
if (files === null) return void 0;
|
|
6993
|
-
if (t === "all") return files.length;
|
|
6994
|
-
return files.filter((f) => f.kind === t).length;
|
|
6995
|
-
};
|
|
6996
|
-
const tabs = TABS.map((t) => ({ ...t, count: countOf(t.value) }));
|
|
6997
|
-
const uploadActions = [{
|
|
6998
|
-
id: "upload",
|
|
6999
|
-
label: uploading ? "Enviando\u2026" : "Enviar arquivos",
|
|
7000
|
-
icon: Upload,
|
|
7001
|
-
intent: "primary",
|
|
7002
|
-
// It puts a new file in the library — the FAB is a "+", not a "⋮".
|
|
7003
|
-
creates: true,
|
|
7004
|
-
disabled: uploading,
|
|
7005
|
-
onSelect: () => uploadInputRef.current?.click()
|
|
7006
|
-
}];
|
|
7007
|
-
const renderGrid = (list) => /* @__PURE__ */ jsx("div", { className: "fz-cards gap-4", children: list.map((file) => /* @__PURE__ */ jsx(
|
|
7008
|
-
MediaCard,
|
|
7009
|
-
{
|
|
7010
|
-
file,
|
|
7011
|
-
dimensions: dimensions[file.id],
|
|
7012
|
-
onMeasured: (d) => measure(file.id, d),
|
|
7013
|
-
onOpen: () => setOpenId(file.id),
|
|
7014
|
-
onDelete: canDelete(file) ? () => setPendingDelete(file) : void 0
|
|
7015
|
-
},
|
|
7016
|
-
file.id
|
|
7017
|
-
)) });
|
|
7018
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7019
|
-
/* @__PURE__ */ jsx(
|
|
7020
|
-
PageTabs,
|
|
7021
|
-
{
|
|
7022
|
-
tabs,
|
|
7023
|
-
value: tab,
|
|
7024
|
-
onChange: setTab,
|
|
7025
|
-
"aria-label": "Arquivos"
|
|
7026
|
-
}
|
|
7027
|
-
),
|
|
7028
|
-
supported && provider.uploadMediaFile && /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PageActions, { actions: uploadActions }) }),
|
|
7029
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
7030
|
-
!supported ? /* @__PURE__ */ jsxs(Card, { className: "p-12 text-center", children: [
|
|
7031
|
-
/* @__PURE__ */ jsx(ImageOff, { className: "mx-auto h-8 w-8 text-muted-foreground" }),
|
|
7032
|
-
/* @__PURE__ */ jsx("p", { className: "mt-3 font-medium", children: "Armazenamento n\xE3o configurado" }),
|
|
7033
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "A biblioteca de arquivos precisa do storage da loja, que n\xE3o est\xE1 dispon\xEDvel neste modo." })
|
|
7034
|
-
] }) : files === null ? /* @__PURE__ */ jsx("div", { className: "fz-cards gap-4", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxs(Card, { className: "p-3", children: [
|
|
7035
|
-
/* @__PURE__ */ jsx("div", { className: "aspect-square animate-pulse rounded-md bg-muted" }),
|
|
7036
|
-
/* @__PURE__ */ jsx("div", { className: "mt-2 h-3.5 w-2/3 animate-pulse rounded bg-muted" })
|
|
7037
|
-
] }, i)) }) : visible.length === 0 ? /* @__PURE__ */ jsxs(Card, { className: "p-12 text-center", children: [
|
|
7038
|
-
/* @__PURE__ */ jsx(ImageOff, { className: "mx-auto h-8 w-8 text-muted-foreground" }),
|
|
7039
|
-
/* @__PURE__ */ jsx("p", { className: "mt-3 font-medium", children: "Nenhum arquivo ainda" }),
|
|
7040
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "As fotos dos seus produtos e o que voc\xEA enviar aqui aparecem nesta biblioteca." })
|
|
7041
|
-
] }) : attached.length && loose.length ? (
|
|
7042
|
-
// Só separa quando há os dois: um título sozinho em cima de tudo é
|
|
7043
|
-
// barulho, e a etiqueta em cada cartão já diz de quem é a foto.
|
|
7044
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
|
|
7045
|
-
/* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
|
|
7046
|
-
/* @__PURE__ */ jsx(SectionTitle, { title: "Fotos de produtos", count: attached.length }),
|
|
7047
|
-
renderGrid(attached)
|
|
7048
|
-
] }),
|
|
7049
|
-
/* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
|
|
7050
|
-
/* @__PURE__ */ jsx(SectionTitle, { title: "Biblioteca", count: loose.length }),
|
|
7051
|
-
renderGrid(loose)
|
|
7052
|
-
] })
|
|
7053
|
-
] })
|
|
7054
|
-
) : renderGrid(visible),
|
|
7055
|
-
/* @__PURE__ */ jsx(
|
|
7056
|
-
DeleteConfirm,
|
|
7057
|
-
{
|
|
7058
|
-
file: pendingDelete,
|
|
7059
|
-
loading: deleting,
|
|
7060
|
-
onConfirm: () => void confirmDelete(),
|
|
7061
|
-
onCancel: () => setPendingDelete(null)
|
|
7062
|
-
}
|
|
7063
|
-
),
|
|
7064
|
-
supported && provider.uploadMediaFile && /* @__PURE__ */ jsx(
|
|
7065
|
-
"input",
|
|
7066
|
-
{
|
|
7067
|
-
ref: uploadInputRef,
|
|
7068
|
-
type: "file",
|
|
7069
|
-
multiple: true,
|
|
7070
|
-
className: "hidden",
|
|
7071
|
-
onChange: (e) => {
|
|
7072
|
-
void handleUpload(e.target.files).finally(() => {
|
|
7073
|
-
if (uploadInputRef.current) uploadInputRef.current.value = "";
|
|
7074
|
-
});
|
|
7075
|
-
}
|
|
7076
|
-
}
|
|
7077
|
-
)
|
|
7078
|
-
] })
|
|
7079
|
-
] });
|
|
7080
|
-
}
|
|
7081
|
-
function DeleteConfirm({ file, loading, onConfirm, onCancel }) {
|
|
7082
|
-
return /* @__PURE__ */ jsx(
|
|
7083
|
-
ConfirmDialog,
|
|
7084
|
-
{
|
|
7085
|
-
open: file !== null,
|
|
7086
|
-
loading,
|
|
7087
|
-
variant: "destructive",
|
|
7088
|
-
title: file?.reference ? "Excluir a foto do produto?" : "Excluir arquivo?",
|
|
7089
|
-
description: file?.reference ? `A foto sai de "${file.reference.label}" e da loja imediatamente.` : "O arquivo sai da biblioteca. Se ele estiver em uso em algum lugar, o endere\xE7o para de funcionar.",
|
|
7090
|
-
confirmLabel: "Excluir",
|
|
7091
|
-
cancelLabel: "Cancelar",
|
|
7092
|
-
onConfirm,
|
|
7093
|
-
onCancel
|
|
7094
|
-
}
|
|
7095
|
-
);
|
|
7096
|
-
}
|
|
7097
|
-
function SectionTitle({ title, count }) {
|
|
7098
|
-
return /* @__PURE__ */ jsxs("h2", { className: "text-sm font-semibold", children: [
|
|
7099
|
-
title,
|
|
7100
|
-
" ",
|
|
7101
|
-
/* @__PURE__ */ jsxs("span", { className: "font-normal text-muted-foreground", children: [
|
|
7102
|
-
"(",
|
|
7103
|
-
count,
|
|
7104
|
-
")"
|
|
7105
|
-
] })
|
|
7106
|
-
] });
|
|
7107
|
-
}
|
|
7108
|
-
function KindIcon({ kind, className }) {
|
|
7109
|
-
const Icon = kind === "video" ? Film : FileText;
|
|
7110
|
-
return /* @__PURE__ */ jsx(Icon, { className });
|
|
7111
|
-
}
|
|
7112
|
-
function MediaCard({ file, dimensions, onMeasured, onOpen, onDelete }) {
|
|
7113
|
-
return /* @__PURE__ */ jsxs(Card, { className: "group relative overflow-hidden p-3", children: [
|
|
7114
|
-
/* @__PURE__ */ jsxs(
|
|
7115
|
-
Button,
|
|
7116
|
-
{
|
|
7117
|
-
variant: "bare",
|
|
7118
|
-
size: "bare",
|
|
7119
|
-
type: "button",
|
|
7120
|
-
onClick: onOpen,
|
|
7121
|
-
"aria-label": `Abrir ${file.name}`,
|
|
7122
|
-
className: "font-normal gap-0 whitespace-normal relative flex aspect-square w-full items-center justify-center overflow-hidden rounded-md bg-muted",
|
|
7123
|
-
children: [
|
|
7124
|
-
file.kind === "image" ? /* @__PURE__ */ jsx(
|
|
7125
|
-
"img",
|
|
7126
|
-
{
|
|
7127
|
-
src: file.url,
|
|
7128
|
-
alt: file.name,
|
|
7129
|
-
loading: "lazy",
|
|
7130
|
-
className: "h-full w-full object-contain",
|
|
7131
|
-
onLoad: (e) => {
|
|
7132
|
-
const img = e.currentTarget;
|
|
7133
|
-
if (img.naturalWidth) onMeasured({ width: img.naturalWidth, height: img.naturalHeight });
|
|
7134
|
-
}
|
|
7135
|
-
}
|
|
7136
|
-
) : /* @__PURE__ */ jsx(KindIcon, { kind: file.kind, className: "h-8 w-8 text-muted-foreground" }),
|
|
7137
|
-
file.reference?.isCover && /* @__PURE__ */ jsx("span", { className: "absolute left-1.5 top-1.5 rounded-full bg-primary px-2 py-0.5 text-[11px] font-bold text-primary-foreground", children: "Capa" })
|
|
7138
|
-
]
|
|
7139
|
-
}
|
|
7140
|
-
),
|
|
7141
|
-
onDelete && /* @__PURE__ */ jsx(Hint, { label: "Excluir", children: /* @__PURE__ */ jsx(
|
|
7142
|
-
Button,
|
|
7143
|
-
{
|
|
7144
|
-
variant: "bare",
|
|
7145
|
-
size: "bare",
|
|
7146
|
-
type: "button",
|
|
7147
|
-
"aria-label": "Excluir",
|
|
7148
|
-
onClick: onDelete,
|
|
7149
|
-
className: "absolute right-4 top-4 rounded-md bg-card/90 p-1.5 text-muted-foreground opacity-0 shadow-button transition-opacity hover:text-destructive group-hover:opacity-100",
|
|
7150
|
-
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
7151
|
-
}
|
|
7152
|
-
) }),
|
|
7153
|
-
/* @__PURE__ */ jsx("p", { className: "mt-2 truncate text-[13px] font-medium", title: file.name, children: file.name }),
|
|
7154
|
-
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: /* @__PURE__ */ jsx(FileMeta, { file, dimensions }) }),
|
|
7155
|
-
isLowResolution(file, dimensions) && /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] font-medium text-warning", children: "Baixa resolu\xE7\xE3o" }),
|
|
7156
|
-
file.reference ? /* @__PURE__ */ jsx(RecordLink, { reference: file.reference, className: "mt-1.5 text-[11px]" }) : /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-[11px] text-muted-foreground", children: "Biblioteca" })
|
|
7157
|
-
] });
|
|
7158
|
-
}
|
|
7159
|
-
function MediaDetail({
|
|
7160
|
-
file,
|
|
7161
|
-
dimensions,
|
|
7162
|
-
onMeasured,
|
|
7163
|
-
position,
|
|
7164
|
-
onNavigate,
|
|
7165
|
-
onBack,
|
|
7166
|
-
onReplace,
|
|
7167
|
-
onDelete,
|
|
7168
|
-
replacing
|
|
7169
|
-
}) {
|
|
7170
|
-
const replaceRef = React14__default.useRef(null);
|
|
7171
|
-
React14__default.useEffect(() => {
|
|
7172
|
-
function onKey(e) {
|
|
7173
|
-
if (e.key === "ArrowLeft") onNavigate(-1);
|
|
7174
|
-
if (e.key === "ArrowRight") onNavigate(1);
|
|
7175
|
-
}
|
|
7176
|
-
document.addEventListener("keydown", onKey);
|
|
7177
|
-
return () => document.removeEventListener("keydown", onKey);
|
|
7178
|
-
}, [onNavigate]);
|
|
7179
|
-
return (
|
|
7180
|
-
// `fz-container` é o ponto de MEDIÇÃO: sem ele a container query de
|
|
7181
|
-
// `fz-split` nunca casa e as duas colunas ficam empilhadas para sempre,
|
|
7182
|
-
// por mais larga que a janela esteja.
|
|
7183
|
-
/* @__PURE__ */ jsxs("div", { className: "fz-container space-y-4", children: [
|
|
7184
|
-
/* @__PURE__ */ jsx(PageHeader, { title: file.name, parentLabel: "Conte\xFAdo", onBack }),
|
|
7185
|
-
position.total > 1 && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
7186
|
-
/* @__PURE__ */ jsx(
|
|
7187
|
-
Button,
|
|
7188
|
-
{
|
|
7189
|
-
variant: "bare",
|
|
7190
|
-
size: "bare",
|
|
7191
|
-
type: "button",
|
|
7192
|
-
"aria-label": "Arquivo anterior",
|
|
7193
|
-
disabled: position.index === 0,
|
|
7194
|
-
onClick: () => onNavigate(-1),
|
|
7195
|
-
className: "rounded-md border p-1 transition hover:bg-muted disabled:opacity-40",
|
|
7196
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
7197
|
-
}
|
|
7198
|
-
),
|
|
7199
|
-
/* @__PURE__ */ jsxs("span", { className: "px-1 tabular-nums", children: [
|
|
7200
|
-
position.index + 1,
|
|
7201
|
-
" de ",
|
|
7202
|
-
position.total
|
|
7203
|
-
] }),
|
|
7204
|
-
/* @__PURE__ */ jsx(
|
|
7205
|
-
Button,
|
|
7206
|
-
{
|
|
7207
|
-
variant: "bare",
|
|
7208
|
-
size: "bare",
|
|
7209
|
-
type: "button",
|
|
7210
|
-
"aria-label": "Pr\xF3ximo arquivo",
|
|
7211
|
-
disabled: position.index === position.total - 1,
|
|
7212
|
-
onClick: () => onNavigate(1),
|
|
7213
|
-
className: "rounded-md border p-1 transition hover:bg-muted disabled:opacity-40",
|
|
7214
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
7215
|
-
}
|
|
7216
|
-
)
|
|
7217
|
-
] }) }),
|
|
7218
|
-
/* @__PURE__ */ jsxs("div", { className: "fz-split", children: [
|
|
7219
|
-
/* @__PURE__ */ jsx(Card, { className: "flex min-h-[320px] items-center justify-center overflow-hidden bg-muted p-2", children: file.kind === "image" ? /* @__PURE__ */ jsx(
|
|
7220
|
-
"img",
|
|
7221
|
-
{
|
|
7222
|
-
src: file.url,
|
|
7223
|
-
alt: file.name,
|
|
7224
|
-
className: "max-h-[70vh] w-auto max-w-full object-contain",
|
|
7225
|
-
onLoad: (e) => {
|
|
7226
|
-
const img = e.currentTarget;
|
|
7227
|
-
if (img.naturalWidth) onMeasured({ width: img.naturalWidth, height: img.naturalHeight });
|
|
7228
|
-
}
|
|
7229
|
-
},
|
|
7230
|
-
file.url
|
|
7231
|
-
) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 p-12 text-muted-foreground", children: [
|
|
7232
|
-
/* @__PURE__ */ jsx(KindIcon, { kind: file.kind, className: "h-10 w-10" }),
|
|
7233
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm", children: "Pr\xE9-visualiza\xE7\xE3o n\xE3o dispon\xEDvel" })
|
|
7234
|
-
] }) }),
|
|
7235
|
-
/* @__PURE__ */ jsxs(Card, { className: "space-y-4 p-4", children: [
|
|
7236
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
7237
|
-
/* @__PURE__ */ jsx("p", { className: "break-words font-medium", title: file.name, children: file.name }),
|
|
7238
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: /* @__PURE__ */ jsx(FileMeta, { file, dimensions }) }),
|
|
7239
|
-
isLowResolution(file, dimensions) && /* @__PURE__ */ jsxs("p", { className: "mt-2 rounded-md bg-warning/10 px-2 py-1.5 text-xs font-medium text-warning", children: [
|
|
7240
|
-
"Baixa resolu\xE7\xE3o \u2014 no zoom da loja essa foto fica borrada. Troque por uma de pelo menos ",
|
|
7241
|
-
LOW_RESOLUTION_PX,
|
|
7242
|
-
"px no lado maior."
|
|
7243
|
-
] })
|
|
7244
|
-
] }),
|
|
7245
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
7246
|
-
/* @__PURE__ */ jsx("p", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: "Onde \xE9 usada" }),
|
|
7247
|
-
file.reference ? /* @__PURE__ */ jsx(RecordLink, { reference: file.reference, className: "mt-1 text-sm" }) : /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Biblioteca \u2014 este arquivo n\xE3o est\xE1 preso a nenhum registro." }),
|
|
7248
|
-
file.reference?.isCover && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "\xC9 a capa: \xE9 ela que aparece no cat\xE1logo, no carrinho e no pedido." })
|
|
7249
|
-
] }),
|
|
7250
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
7251
|
-
file.kind === "image" && /* @__PURE__ */ jsx(
|
|
7252
|
-
AskAIButton,
|
|
7253
|
-
{
|
|
7254
|
-
className: "w-full",
|
|
7255
|
-
size: "lg",
|
|
7256
|
-
label: "Melhorar com IA",
|
|
7257
|
-
media: { url: file.url, name: file.name },
|
|
7258
|
-
draft: "Melhora essa imagem",
|
|
7259
|
-
hint: "o que voc\xEA quer mudar nessa foto",
|
|
7260
|
-
intro: {
|
|
7261
|
-
persona: "Est\xFAdio de imagem",
|
|
7262
|
-
pitch: `Vamos melhorar ${file.name}.`,
|
|
7263
|
-
description: "Posso limpar o fundo, corrigir luz e cor, e gerar uma vers\xE3o em resolu\xE7\xE3o maior para o cat\xE1logo.",
|
|
7264
|
-
suggestions: [
|
|
7265
|
-
{ label: "Fundo branco", prompt: "Remove o fundo e deixa branco" },
|
|
7266
|
-
{ label: "Mais n\xEDtida", prompt: "Aumenta a resolu\xE7\xE3o e a nitidez" },
|
|
7267
|
-
{ label: "Corrigir a luz", prompt: "Corrige a ilumina\xE7\xE3o e as cores" }
|
|
7268
|
-
]
|
|
7269
|
-
}
|
|
7270
|
-
}
|
|
7271
|
-
),
|
|
7272
|
-
/* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", className: "w-full", children: /* @__PURE__ */ jsxs("a", { href: downloadUrlOf(file), download: file.name, children: [
|
|
7273
|
-
/* @__PURE__ */ jsx(Download, { className: "mr-2 h-4 w-4" }),
|
|
7274
|
-
"Baixar"
|
|
7275
|
-
] }) }),
|
|
7276
|
-
onReplace && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7277
|
-
/* @__PURE__ */ jsx(
|
|
7278
|
-
"input",
|
|
7279
|
-
{
|
|
7280
|
-
ref: replaceRef,
|
|
7281
|
-
type: "file",
|
|
7282
|
-
accept: file.kind === "image" ? "image/*" : void 0,
|
|
7283
|
-
className: "hidden",
|
|
7284
|
-
onChange: (e) => {
|
|
7285
|
-
const picked = e.target.files?.[0];
|
|
7286
|
-
e.target.value = "";
|
|
7287
|
-
if (picked) onReplace(picked);
|
|
7288
|
-
}
|
|
7289
|
-
}
|
|
7290
|
-
),
|
|
7291
|
-
/* @__PURE__ */ jsxs(
|
|
7292
|
-
Button,
|
|
7293
|
-
{
|
|
7294
|
-
variant: "outline",
|
|
7295
|
-
className: "w-full",
|
|
7296
|
-
disabled: replacing,
|
|
7297
|
-
onClick: () => replaceRef.current?.click(),
|
|
7298
|
-
children: [
|
|
7299
|
-
/* @__PURE__ */ jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${replacing ? "animate-spin" : ""}` }),
|
|
7300
|
-
replacing ? "Trocando\u2026" : "Trocar arquivo"
|
|
7301
|
-
]
|
|
7302
|
-
}
|
|
7303
|
-
),
|
|
7304
|
-
file.reference && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: "A troca mant\xE9m a posi\xE7\xE3o da foto na galeria do produto." })
|
|
7305
|
-
] }),
|
|
7306
|
-
onDelete && /* @__PURE__ */ jsxs(Button, { variant: "ghost", className: "w-full text-destructive hover:text-destructive", onClick: onDelete, children: [
|
|
7307
|
-
/* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
|
|
7308
|
-
"Excluir"
|
|
7309
|
-
] })
|
|
7310
|
-
] })
|
|
7311
|
-
] })
|
|
7312
|
-
] })
|
|
7313
|
-
] })
|
|
7314
|
-
);
|
|
7315
|
-
}
|
|
7316
|
-
function RecordLink({ reference, className = "" }) {
|
|
7317
|
-
return /* @__PURE__ */ jsxs(
|
|
7318
|
-
"a",
|
|
7319
|
-
{
|
|
7320
|
-
href: recordRoute(reference),
|
|
7321
|
-
title: `Abrir ${reference.label}`,
|
|
7322
|
-
className: `flex items-center gap-1 truncate font-medium text-primary hover:underline ${className}`,
|
|
7323
|
-
children: [
|
|
7324
|
-
/* @__PURE__ */ jsx(Package, { className: "h-3 w-3 shrink-0" }),
|
|
7325
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: reference.label })
|
|
7326
|
-
]
|
|
7327
|
-
}
|
|
7328
|
-
);
|
|
7329
|
-
}
|
|
7330
|
-
function FileMeta({ file, dimensions }) {
|
|
7331
|
-
const parts = [
|
|
7332
|
-
file.extension,
|
|
7333
|
-
dimensions ? `${dimensions.width}\xD7${dimensions.height}` : "",
|
|
7334
|
-
formatSize(file.size)
|
|
7335
|
-
].filter(Boolean);
|
|
7336
|
-
return /* @__PURE__ */ jsx(Fragment, { children: parts.join(" \xB7 ") });
|
|
7337
|
-
}
|
|
7338
|
-
function isLowResolution(file, dimensions) {
|
|
7339
|
-
return file.kind === "image" && !!dimensions && Math.max(dimensions.width, dimensions.height) < LOW_RESOLUTION_PX;
|
|
7340
|
-
}
|
|
7341
|
-
|
|
7342
4301
|
// src/integrations/bling/limits.ts
|
|
7343
4302
|
var BLING_CONNECTOR_ID = "bling";
|
|
7344
4303
|
var BLING_RATE_LIMIT = { perSecond: 3, perDay: 12e4 };
|
|
@@ -9025,312 +5984,15 @@ function advanceNeoChefCursor(products, overlapMs) {
|
|
|
9025
5984
|
if (stamps.length === 0) return null;
|
|
9026
5985
|
return new Date(Math.max(...stamps) - overlapMs).toISOString();
|
|
9027
5986
|
}
|
|
9028
|
-
function ProductContentTab({ item, getProvider, libraryHref = "#/content" }) {
|
|
9029
|
-
const catalogId = item?.id;
|
|
9030
|
-
const [product, setProduct] = useState(void 0);
|
|
9031
|
-
const [error, setError] = useState(null);
|
|
9032
|
-
useEffect(() => {
|
|
9033
|
-
if (!catalogId) {
|
|
9034
|
-
setProduct(null);
|
|
9035
|
-
return;
|
|
9036
|
-
}
|
|
9037
|
-
let alive = true;
|
|
9038
|
-
setProduct(void 0);
|
|
9039
|
-
const provider = getProvider();
|
|
9040
|
-
const lookup = provider.getProductByCatalogId ? provider.getProductByCatalogId(catalogId) : provider.getProduct(catalogId);
|
|
9041
|
-
lookup.then((found) => {
|
|
9042
|
-
if (alive) setProduct(found);
|
|
9043
|
-
}).catch((err) => {
|
|
9044
|
-
if (alive) {
|
|
9045
|
-
setProduct(null);
|
|
9046
|
-
setError(errorMessage(err));
|
|
9047
|
-
}
|
|
9048
|
-
});
|
|
9049
|
-
return () => {
|
|
9050
|
-
alive = false;
|
|
9051
|
-
};
|
|
9052
|
-
}, [catalogId]);
|
|
9053
|
-
if (product === void 0) {
|
|
9054
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", "aria-busy": "true", children: [
|
|
9055
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
9056
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-40" }),
|
|
9057
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full max-w-lg" })
|
|
9058
|
-
] }),
|
|
9059
|
-
/* @__PURE__ */ jsx("ul", { className: "fz-cards gap-3", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsxs("li", { className: "overflow-hidden rounded-lg border", children: [
|
|
9060
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "aspect-square w-full rounded-none" }),
|
|
9061
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-2 py-2", children: [
|
|
9062
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-14" }),
|
|
9063
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-20" })
|
|
9064
|
-
] })
|
|
9065
|
-
] }, i)) })
|
|
9066
|
-
] });
|
|
9067
|
-
}
|
|
9068
|
-
if (!product) {
|
|
9069
|
-
return /* @__PURE__ */ jsx("p", { className: "rounded-lg border border-dashed px-4 py-10 text-center text-sm text-muted-foreground", children: error ?? "Este produto ainda n\xE3o est\xE1 na loja, ent\xE3o n\xE3o tem galeria. Publique-o na loja para adicionar fotos." });
|
|
9070
|
-
}
|
|
9071
|
-
return /* @__PURE__ */ jsx(
|
|
9072
|
-
ProductImagesTab,
|
|
9073
|
-
{
|
|
9074
|
-
item: product,
|
|
9075
|
-
getProvider,
|
|
9076
|
-
footer: /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
9077
|
-
"Estas fotos tamb\xE9m aparecem em",
|
|
9078
|
-
" ",
|
|
9079
|
-
/* @__PURE__ */ jsx("a", { href: libraryHref, className: "font-medium text-primary underline underline-offset-2 hover:opacity-80", children: "Conte\xFAdo" }),
|
|
9080
|
-
", junto com as de todos os produtos."
|
|
9081
|
-
] })
|
|
9082
|
-
}
|
|
9083
|
-
);
|
|
9084
|
-
}
|
|
9085
|
-
var SYMBOLS = { BRL: "R$", EUR: "\u20AC", USD: "US$", GBP: "\xA3", ARS: "AR$", MXN: "MX$" };
|
|
9086
|
-
function ProductMarketPricesTab({ item, getProvider, settingsHref = "#/settings/shop-markets" }) {
|
|
9087
|
-
const catalogId = item?.id;
|
|
9088
|
-
const basePrice = Number(item?.salePrice ?? item?.price ?? 0);
|
|
9089
|
-
const [markets, setMarkets] = useState(void 0);
|
|
9090
|
-
const [saved, setSaved] = useState({});
|
|
9091
|
-
const [draft2, setDraft] = useState({});
|
|
9092
|
-
const [error, setError] = useState(null);
|
|
9093
|
-
const [saving, setSaving] = useState(false);
|
|
9094
|
-
const [done, setDone] = useState(false);
|
|
9095
|
-
useEffect(() => {
|
|
9096
|
-
if (!catalogId) return;
|
|
9097
|
-
let alive = true;
|
|
9098
|
-
const provider = getProvider();
|
|
9099
|
-
if (!provider.listAdminMarkets || !provider.getCatalogMarketPrices) {
|
|
9100
|
-
setMarkets([]);
|
|
9101
|
-
return;
|
|
9102
|
-
}
|
|
9103
|
-
Promise.all([provider.listAdminMarkets(), provider.getCatalogMarketPrices(catalogId)]).then(([list, prices]) => {
|
|
9104
|
-
if (!alive) return;
|
|
9105
|
-
const byMarket = Object.fromEntries(prices.map((p) => [p.marketId, p.price]));
|
|
9106
|
-
setMarkets(list);
|
|
9107
|
-
setSaved(byMarket);
|
|
9108
|
-
setDraft(byMarket);
|
|
9109
|
-
}).catch((err) => {
|
|
9110
|
-
if (alive) {
|
|
9111
|
-
setMarkets([]);
|
|
9112
|
-
setError(errorMessage(err));
|
|
9113
|
-
}
|
|
9114
|
-
});
|
|
9115
|
-
return () => {
|
|
9116
|
-
alive = false;
|
|
9117
|
-
};
|
|
9118
|
-
}, [catalogId]);
|
|
9119
|
-
const dirty = useMemo(
|
|
9120
|
-
() => Object.keys(draft2).some((id) => (draft2[id] ?? null) !== (saved[id] ?? null)),
|
|
9121
|
-
[draft2, saved]
|
|
9122
|
-
);
|
|
9123
|
-
async function save() {
|
|
9124
|
-
if (!catalogId) return;
|
|
9125
|
-
const provider = getProvider();
|
|
9126
|
-
if (!provider.setCatalogMarketPrice) return;
|
|
9127
|
-
setSaving(true);
|
|
9128
|
-
setError(null);
|
|
9129
|
-
setDone(false);
|
|
9130
|
-
try {
|
|
9131
|
-
for (const id of Object.keys(draft2)) {
|
|
9132
|
-
const next = draft2[id] ?? null;
|
|
9133
|
-
if (next !== (saved[id] ?? null)) await provider.setCatalogMarketPrice(catalogId, id, next);
|
|
9134
|
-
}
|
|
9135
|
-
setSaved({ ...draft2 });
|
|
9136
|
-
setDone(true);
|
|
9137
|
-
} catch (err) {
|
|
9138
|
-
setError(errorMessage(err));
|
|
9139
|
-
} finally {
|
|
9140
|
-
setSaving(false);
|
|
9141
|
-
}
|
|
9142
|
-
}
|
|
9143
|
-
if (markets === void 0) {
|
|
9144
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", "aria-busy": "true", children: [
|
|
9145
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-48" }),
|
|
9146
|
-
[0, 1].map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-16 w-full" }, i))
|
|
9147
|
-
] });
|
|
9148
|
-
}
|
|
9149
|
-
if (markets.length === 0) {
|
|
9150
|
-
return /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed px-4 py-10 text-center text-sm text-muted-foreground", children: error ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9151
|
-
"A loja vende numa moeda s\xF3. Para vender em outros pa\xEDses com pre\xE7o pr\xF3prio,",
|
|
9152
|
-
" ",
|
|
9153
|
-
/* @__PURE__ */ jsx("a", { href: settingsHref, className: "font-medium text-primary underline underline-offset-2", children: "crie os mercados" }),
|
|
9154
|
-
"."
|
|
9155
|
-
] }) });
|
|
9156
|
-
}
|
|
9157
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
9158
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
9159
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Pre\xE7os por mercado" }),
|
|
9160
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: "O pre\xE7o deste produto em cada mercado, na moeda dele. No mercado padr\xE3o, vazio usa o pre\xE7o de venda do produto; nos outros, vazio quer dizer que o produto n\xE3o \xE9 vendido l\xE1." })
|
|
9161
|
-
] }),
|
|
9162
|
-
/* @__PURE__ */ jsx("ul", { className: "divide-y rounded-lg border", children: markets.map((market) => {
|
|
9163
|
-
const value = draft2[market.id] ?? null;
|
|
9164
|
-
const symbol = SYMBOLS[market.currency] ?? market.currency;
|
|
9165
|
-
return /* @__PURE__ */ jsxs("li", { className: "flex flex-wrap items-center gap-4 px-4 py-3", children: [
|
|
9166
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-[12rem] flex-1", children: [
|
|
9167
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", children: [
|
|
9168
|
-
market.name,
|
|
9169
|
-
/* @__PURE__ */ jsx(Badge, { variant: "secondary", children: market.currency }),
|
|
9170
|
-
market.isDefault && /* @__PURE__ */ jsx(Badge, { variant: "outline", children: "Padr\xE3o" }),
|
|
9171
|
-
!market.isActive && /* @__PURE__ */ jsx(Badge, { variant: "outline", children: "Inativo" })
|
|
9172
|
-
] }),
|
|
9173
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: market.countries.length ? market.countries.join(", ") : "Todos os pa\xEDses sem outro mercado" })
|
|
9174
|
-
] }),
|
|
9175
|
-
/* @__PURE__ */ jsx("div", { className: "w-48", children: /* @__PURE__ */ jsx(
|
|
9176
|
-
CurrencyInput,
|
|
9177
|
-
{
|
|
9178
|
-
value: value ?? 0,
|
|
9179
|
-
onChange: (next) => {
|
|
9180
|
-
setDone(false);
|
|
9181
|
-
setDraft((d) => ({ ...d, [market.id]: next > 0 ? next : null }));
|
|
9182
|
-
},
|
|
9183
|
-
symbol,
|
|
9184
|
-
currencyCode: market.currency,
|
|
9185
|
-
locale: market.locale ?? "pt-BR",
|
|
9186
|
-
placeholder: market.isDefault && basePrice ? String(basePrice) : void 0
|
|
9187
|
-
}
|
|
9188
|
-
) }),
|
|
9189
|
-
/* @__PURE__ */ jsx("p", { className: "w-full text-xs sm:w-56", children: value != null ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Pre\xE7o pr\xF3prio neste mercado." }) : market.isDefault ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Usa o pre\xE7o de venda do produto." }) : /* @__PURE__ */ jsx("span", { className: "text-amber-600", children: "Sem pre\xE7o \u2014 n\xE3o aparece \xE0 venda neste mercado." }) })
|
|
9190
|
-
] }, market.id);
|
|
9191
|
-
}) }),
|
|
9192
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
9193
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
9194
|
-
/* @__PURE__ */ jsx(Button, { onClick: () => {
|
|
9195
|
-
void save();
|
|
9196
|
-
}, disabled: !dirty || saving, children: saving ? "Salvando\u2026" : "Salvar pre\xE7os" }),
|
|
9197
|
-
done && !dirty && /* @__PURE__ */ jsx("span", { className: "text-sm text-emerald-600", children: "Pre\xE7os salvos." }),
|
|
9198
|
-
/* @__PURE__ */ jsx("a", { href: settingsHref, className: "ml-auto text-xs text-muted-foreground underline underline-offset-2", children: "Gerenciar mercados" })
|
|
9199
|
-
] })
|
|
9200
|
-
] });
|
|
9201
|
-
}
|
|
9202
|
-
var NO_CATEGORY = "__none__";
|
|
9203
|
-
var STATUS_LABEL3 = {
|
|
9204
|
-
active: { label: "\xC0 venda na loja", variant: "success" },
|
|
9205
|
-
draft: { label: "Rascunho", variant: "secondary" },
|
|
9206
|
-
archived: { label: "Fora da loja", variant: "outline" }
|
|
9207
|
-
};
|
|
9208
|
-
function ProductListingTab({ item, getProvider }) {
|
|
9209
|
-
const catalog = item ?? {};
|
|
9210
|
-
const catalogId = catalog.id;
|
|
9211
|
-
const [listing, setListing] = useState(void 0);
|
|
9212
|
-
const [categories, setCategories] = useState([]);
|
|
9213
|
-
const [categoryId, setCategoryId] = useState(NO_CATEGORY);
|
|
9214
|
-
const [busy, setBusy] = useState(false);
|
|
9215
|
-
const [error, setError] = useState(null);
|
|
9216
|
-
useEffect(() => {
|
|
9217
|
-
if (!catalogId) {
|
|
9218
|
-
setListing(null);
|
|
9219
|
-
return;
|
|
9220
|
-
}
|
|
9221
|
-
let alive = true;
|
|
9222
|
-
setListing(void 0);
|
|
9223
|
-
const provider = getProvider();
|
|
9224
|
-
const lookup = provider.getProductByCatalogId ? provider.getProductByCatalogId(catalogId) : Promise.resolve(null);
|
|
9225
|
-
Promise.all([lookup, provider.listCategories().catch(() => [])]).then(([found, cats]) => {
|
|
9226
|
-
if (!alive) return;
|
|
9227
|
-
setListing(found);
|
|
9228
|
-
setCategories(cats);
|
|
9229
|
-
setCategoryId(found?.categoryId ?? NO_CATEGORY);
|
|
9230
|
-
}).catch((err) => {
|
|
9231
|
-
if (alive) {
|
|
9232
|
-
setListing(null);
|
|
9233
|
-
setError(errorMessage(err));
|
|
9234
|
-
}
|
|
9235
|
-
});
|
|
9236
|
-
return () => {
|
|
9237
|
-
alive = false;
|
|
9238
|
-
};
|
|
9239
|
-
}, [catalogId]);
|
|
9240
|
-
async function publish() {
|
|
9241
|
-
if (!catalogId || !catalog.name) return;
|
|
9242
|
-
setBusy(true);
|
|
9243
|
-
setError(null);
|
|
9244
|
-
try {
|
|
9245
|
-
const created = await getProvider().createProduct({
|
|
9246
|
-
catalogProductId: catalogId,
|
|
9247
|
-
name: catalog.name,
|
|
9248
|
-
description: catalog.description ?? void 0,
|
|
9249
|
-
sku: catalog.sku ?? void 0,
|
|
9250
|
-
price: Number(catalog.salePrice ?? catalog.price ?? 0),
|
|
9251
|
-
// An inactive record goes in as a draft: listed, not sold, until the
|
|
9252
|
-
// record's Ativo switch is turned on.
|
|
9253
|
-
status: catalog.isActive === false ? "draft" : "active",
|
|
9254
|
-
categoryId: categoryId === NO_CATEGORY ? void 0 : categoryId
|
|
9255
|
-
});
|
|
9256
|
-
setListing(created);
|
|
9257
|
-
} catch (err) {
|
|
9258
|
-
setError(errorMessage(err));
|
|
9259
|
-
} finally {
|
|
9260
|
-
setBusy(false);
|
|
9261
|
-
}
|
|
9262
|
-
}
|
|
9263
|
-
async function changeCategory(next) {
|
|
9264
|
-
if (!listing) return;
|
|
9265
|
-
const previous = categoryId;
|
|
9266
|
-
setCategoryId(next);
|
|
9267
|
-
setError(null);
|
|
9268
|
-
try {
|
|
9269
|
-
const updated = await getProvider().updateProduct(listing.id, { categoryId: next === NO_CATEGORY ? null : next });
|
|
9270
|
-
setListing(updated);
|
|
9271
|
-
} catch (err) {
|
|
9272
|
-
setCategoryId(previous);
|
|
9273
|
-
setError(errorMessage(err));
|
|
9274
|
-
}
|
|
9275
|
-
}
|
|
9276
|
-
if (listing === void 0) {
|
|
9277
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", "aria-busy": "true", children: [
|
|
9278
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-48" }),
|
|
9279
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-24 w-full" })
|
|
9280
|
-
] });
|
|
9281
|
-
}
|
|
9282
|
-
const categoryPicker = /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
9283
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Categoria na loja" }),
|
|
9284
|
-
/* @__PURE__ */ jsxs(Select, { value: categoryId, onValueChange: (v) => {
|
|
9285
|
-
if (listing) void changeCategory(v);
|
|
9286
|
-
else setCategoryId(v);
|
|
9287
|
-
}, children: [
|
|
9288
|
-
/* @__PURE__ */ jsx(SelectTrigger, { className: "w-72", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Sem categoria" }) }),
|
|
9289
|
-
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
9290
|
-
/* @__PURE__ */ jsx(SelectItem, { value: NO_CATEGORY, children: "Sem categoria" }),
|
|
9291
|
-
categories.map((c) => /* @__PURE__ */ jsx(SelectItem, { value: c.id, children: c.name }, c.id))
|
|
9292
|
-
] })
|
|
9293
|
-
] })
|
|
9294
|
-
] });
|
|
9295
|
-
if (!listing) {
|
|
9296
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
9297
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
9298
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Loja e delivery" }),
|
|
9299
|
-
/* @__PURE__ */ jsxs("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
9300
|
-
"Este produto ainda n\xE3o est\xE1 na loja. Ao colocar, ele aparece no site e no delivery com o nome, o pre\xE7o e a descri\xE7\xE3o deste cadastro",
|
|
9301
|
-
catalog.isActive === false ? " \u2014 como rascunho, at\xE9 o produto ser ativado" : "",
|
|
9302
|
-
"."
|
|
9303
|
-
] })
|
|
9304
|
-
] }),
|
|
9305
|
-
categoryPicker,
|
|
9306
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
9307
|
-
/* @__PURE__ */ jsx(Button, { onClick: () => {
|
|
9308
|
-
void publish();
|
|
9309
|
-
}, disabled: busy || !catalog.name, children: busy ? "Colocando\u2026" : "Colocar na loja" })
|
|
9310
|
-
] });
|
|
9311
|
-
}
|
|
9312
|
-
const status = STATUS_LABEL3[listing.status] ?? { label: listing.status, variant: "outline" };
|
|
9313
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
9314
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
9315
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
9316
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Loja e delivery" }),
|
|
9317
|
-
/* @__PURE__ */ jsx(Badge, { variant: status.variant, children: status.label })
|
|
9318
|
-
] }),
|
|
9319
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: "Nome, pre\xE7o e descri\xE7\xE3o v\xEAm deste cadastro. Para tirar o produto da loja, desative-o no cadastro; ativ\xE1-lo de novo o devolve \xE0 venda. Fotos ficam na aba Conte\xFAdo." })
|
|
9320
|
-
] }),
|
|
9321
|
-
categoryPicker,
|
|
9322
|
-
error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error })
|
|
9323
|
-
] });
|
|
9324
|
-
}
|
|
9325
5987
|
|
|
9326
5988
|
// src/index.ts
|
|
9327
|
-
var ShopPage =
|
|
9328
|
-
var LazyOrderDetail =
|
|
5989
|
+
var ShopPage = React.lazy(() => import('./ShopPage-NARKHXOO.js').then((m) => ({ default: m.ShopPage })));
|
|
5990
|
+
var LazyOrderDetail = React.lazy(() => import('./ShopPage-NARKHXOO.js').then((m) => ({ default: m.ShopOrderDetailSlot })));
|
|
9329
5991
|
function ShopOrderDetail(provider) {
|
|
9330
|
-
const Detail = (props) =>
|
|
9331
|
-
|
|
5992
|
+
const Detail = (props) => React.createElement(
|
|
5993
|
+
React.Suspense,
|
|
9332
5994
|
{ fallback: null },
|
|
9333
|
-
|
|
5995
|
+
React.createElement(LazyOrderDetail, { ...props, provider })
|
|
9334
5996
|
);
|
|
9335
5997
|
Detail.displayName = "ShopOrderDetail";
|
|
9336
5998
|
return Detail;
|
|
@@ -9343,10 +6005,10 @@ function createShopPlugin(options) {
|
|
|
9343
6005
|
registerEntityPath("shop_customer", "/customers");
|
|
9344
6006
|
registerPersonLookup("shop_customer", shopCustomerLookup(() => resolveProvider()));
|
|
9345
6007
|
const pageFor = (section) => {
|
|
9346
|
-
const Wrapped = () =>
|
|
9347
|
-
|
|
6008
|
+
const Wrapped = () => React.createElement(
|
|
6009
|
+
React.Suspense,
|
|
9348
6010
|
{ fallback: null },
|
|
9349
|
-
|
|
6011
|
+
React.createElement(
|
|
9350
6012
|
ShopPage,
|
|
9351
6013
|
{ provider: options?.provider, section }
|
|
9352
6014
|
)
|
|
@@ -9513,7 +6175,7 @@ function createShopPlugin(options) {
|
|
|
9513
6175
|
dataProvider: createShopGiftCardDataProvider(resolveProvider),
|
|
9514
6176
|
feature: "shop",
|
|
9515
6177
|
stats: (items, total) => {
|
|
9516
|
-
const
|
|
6178
|
+
const money2 = (v) => formatCurrency(v, curr.code);
|
|
9517
6179
|
const active = items?.filter((g) => g.status === "active") ?? [];
|
|
9518
6180
|
return [
|
|
9519
6181
|
{ id: "total", label: "Cart\xF5es emitidos", value: total, icon: CreditCard },
|
|
@@ -9524,18 +6186,18 @@ function createShopPlugin(options) {
|
|
|
9524
6186
|
id: "outstanding",
|
|
9525
6187
|
label: "Saldo em aberto",
|
|
9526
6188
|
icon: CircleDollarSign,
|
|
9527
|
-
value:
|
|
6189
|
+
value: money2(active.reduce((sum, g) => sum + (g.balance ?? 0), 0))
|
|
9528
6190
|
},
|
|
9529
6191
|
{
|
|
9530
6192
|
id: "issued",
|
|
9531
6193
|
label: "Valor emitido",
|
|
9532
6194
|
icon: TicketCheck,
|
|
9533
|
-
value:
|
|
6195
|
+
value: money2(items?.reduce((sum, g) => sum + (g.initialBalance ?? 0), 0) ?? 0)
|
|
9534
6196
|
}
|
|
9535
6197
|
];
|
|
9536
6198
|
}
|
|
9537
6199
|
});
|
|
9538
|
-
const ContentPageWrapped = () =>
|
|
6200
|
+
const ContentPageWrapped = () => React.createElement(ContentPage, { resolveProvider: options?.provider });
|
|
9539
6201
|
ContentPageWrapped.displayName = "ContentPage";
|
|
9540
6202
|
const navSection = options?.navSection ?? "main";
|
|
9541
6203
|
const navPosition = options?.navPosition ?? 6;
|
|
@@ -9625,7 +6287,7 @@ function createShopPlugin(options) {
|
|
|
9625
6287
|
];
|
|
9626
6288
|
const redirectTo = (target) => {
|
|
9627
6289
|
const Redirect = () => {
|
|
9628
|
-
|
|
6290
|
+
React.useEffect(() => {
|
|
9629
6291
|
adminNavigateTo(target);
|
|
9630
6292
|
}, []);
|
|
9631
6293
|
return null;
|
|
@@ -9666,7 +6328,7 @@ function createShopPlugin(options) {
|
|
|
9666
6328
|
{ path: "/orders/:id/edit", component: pageFor("orders") }
|
|
9667
6329
|
];
|
|
9668
6330
|
registerComponent("orders.detail", ShopOrderDetail(options?.provider));
|
|
9669
|
-
const DeliveryStore = () =>
|
|
6331
|
+
const DeliveryStore = () => React.createElement(DeliveryStoreSettings, { provider: options?.provider });
|
|
9670
6332
|
DeliveryStore.displayName = "ShopDeliveryStoreSettings";
|
|
9671
6333
|
registerComponent("shop.delivery-settings", DeliveryStore);
|
|
9672
6334
|
const ACTIVE_LEGACY = withProducts ? LEGACY_ROUTES : LEGACY_ROUTES.map((r) => r.path === "/shop" || r.path === "/shop/products" ? { path: r.path, component: redirectTo("/orders") } : r);
|
|
@@ -9771,7 +6433,7 @@ function createShopPlugin(options) {
|
|
|
9771
6433
|
label: label("store", "Loja"),
|
|
9772
6434
|
icon: "ShoppingBag",
|
|
9773
6435
|
group: COMMERCE_GROUP,
|
|
9774
|
-
component: settingsTab("StoreGeneralSettings", () =>
|
|
6436
|
+
component: settingsTab("StoreGeneralSettings", () => React.createElement(StoreGeneralSettings, { currency: curr, provider: options?.provider })),
|
|
9775
6437
|
order: 30,
|
|
9776
6438
|
permission: READ
|
|
9777
6439
|
},
|
|
@@ -9781,7 +6443,7 @@ function createShopPlugin(options) {
|
|
|
9781
6443
|
label: "Mercados",
|
|
9782
6444
|
icon: "Globe",
|
|
9783
6445
|
group: COMMERCE_GROUP,
|
|
9784
|
-
component: settingsTab("MarketsSettings", () =>
|
|
6446
|
+
component: settingsTab("MarketsSettings", () => React.createElement(MarketsSettings, {})),
|
|
9785
6447
|
order: 30.5,
|
|
9786
6448
|
permission: READ
|
|
9787
6449
|
},
|
|
@@ -9790,7 +6452,7 @@ function createShopPlugin(options) {
|
|
|
9790
6452
|
label: "Pagamentos",
|
|
9791
6453
|
icon: "CreditCard",
|
|
9792
6454
|
group: COMMERCE_GROUP,
|
|
9793
|
-
component: settingsTab("PaymentsSettings", () =>
|
|
6455
|
+
component: settingsTab("PaymentsSettings", () => React.createElement(PaymentsSettings, { provider: options?.provider })),
|
|
9794
6456
|
order: 31,
|
|
9795
6457
|
permission: READ
|
|
9796
6458
|
},
|
|
@@ -9799,7 +6461,7 @@ function createShopPlugin(options) {
|
|
|
9799
6461
|
label: "Checkout",
|
|
9800
6462
|
icon: "ShoppingCart",
|
|
9801
6463
|
group: COMMERCE_GROUP,
|
|
9802
|
-
component: settingsTab("CheckoutSettings", () =>
|
|
6464
|
+
component: settingsTab("CheckoutSettings", () => React.createElement(CheckoutSettings, { provider: options?.provider })),
|
|
9803
6465
|
order: 32,
|
|
9804
6466
|
permission: READ
|
|
9805
6467
|
},
|
|
@@ -9808,7 +6470,7 @@ function createShopPlugin(options) {
|
|
|
9808
6470
|
label: label("shipping", "Entrega"),
|
|
9809
6471
|
icon: "Truck",
|
|
9810
6472
|
group: COMMERCE_GROUP,
|
|
9811
|
-
component: settingsTab("ShippingSettings", () =>
|
|
6473
|
+
component: settingsTab("ShippingSettings", () => React.createElement(ShippingSettings, { provider: options?.provider })),
|
|
9812
6474
|
order: 33,
|
|
9813
6475
|
permission: READ
|
|
9814
6476
|
},
|
|
@@ -9817,7 +6479,7 @@ function createShopPlugin(options) {
|
|
|
9817
6479
|
label: "Fulfillment",
|
|
9818
6480
|
icon: "PackageCheck",
|
|
9819
6481
|
group: COMMERCE_GROUP,
|
|
9820
|
-
component: settingsTab("FulfillmentSettings", () =>
|
|
6482
|
+
component: settingsTab("FulfillmentSettings", () => React.createElement(FulfillmentSettings, { provider: options?.provider })),
|
|
9821
6483
|
order: 34,
|
|
9822
6484
|
permission: READ
|
|
9823
6485
|
}
|
|
@@ -9856,34 +6518,45 @@ function createShopPlugin(options) {
|
|
|
9856
6518
|
integrations: ["shopify", "stripe"]
|
|
9857
6519
|
};
|
|
9858
6520
|
}
|
|
6521
|
+
var ProductContentTabView = lazyComponent(() => import('./ProductContentTab-PSNDS7JZ.js').then((m) => m.ProductContentTab));
|
|
9859
6522
|
function shopProductContentTab(options) {
|
|
9860
6523
|
return {
|
|
9861
6524
|
id: "content",
|
|
9862
6525
|
label: options.label ?? "Conte\xFAdo",
|
|
9863
6526
|
icon: "Image",
|
|
9864
|
-
component:
|
|
6527
|
+
component: ProductContentTabView,
|
|
9865
6528
|
props: { getProvider: options.provider, libraryHref: options.libraryHref ?? "#/content" }
|
|
9866
6529
|
};
|
|
9867
6530
|
}
|
|
6531
|
+
var ProductMarketPricesTabView = lazyComponent(() => import('./ProductMarketPricesTab-QD2ZXEWL.js').then((m) => m.ProductMarketPricesTab));
|
|
9868
6532
|
function shopProductMarketPricesTab(options) {
|
|
9869
6533
|
return {
|
|
9870
6534
|
id: "market-prices",
|
|
9871
6535
|
label: options.label ?? "Pre\xE7os por mercado",
|
|
9872
6536
|
icon: "Globe",
|
|
9873
|
-
component:
|
|
6537
|
+
component: ProductMarketPricesTabView,
|
|
9874
6538
|
props: { getProvider: options.provider, settingsHref: options.settingsHref ?? "#/settings/shop-markets" }
|
|
9875
6539
|
};
|
|
9876
6540
|
}
|
|
6541
|
+
var ProductListingTabView = lazyComponent(() => import('./ProductListingTab-55Y5RXU7.js').then((m) => m.ProductListingTab));
|
|
6542
|
+
var ContentPage = React.lazy(() => import('./ContentPage-2PHOZSFZ.js').then((m) => ({ default: m.ContentPage })));
|
|
6543
|
+
var StoreGeneralSettings = lazyComponent(() => import('./StoreGeneralSettings-DEZ2CMVG.js').then((m) => m.StoreGeneralSettings));
|
|
6544
|
+
var PaymentsSettings = lazyComponent(() => import('./PaymentsSettings-5CI74TY2.js').then((m) => m.PaymentsSettings));
|
|
6545
|
+
var CheckoutSettings = lazyComponent(() => import('./CheckoutSettings-GQRYZF6I.js').then((m) => m.CheckoutSettings));
|
|
6546
|
+
var ShippingSettings = lazyComponent(() => import('./ShippingSettings-UBTNRBJ4.js').then((m) => m.ShippingSettings));
|
|
6547
|
+
var DeliveryStoreSettings = lazyComponent(() => import('./DeliveryStoreSettings-L2RXOVB6.js').then((m) => m.DeliveryStoreSettings));
|
|
6548
|
+
var FulfillmentSettings = lazyComponent(() => import('./FulfillmentSettings-PW233UOK.js').then((m) => m.FulfillmentSettings));
|
|
6549
|
+
var MarketsSettings = lazyComponent(() => import('./MarketsSettings-YUEVDLIR.js').then((m) => m.MarketsSettings));
|
|
9877
6550
|
function shopProductListingTab(options) {
|
|
9878
6551
|
return {
|
|
9879
6552
|
id: "store-listing",
|
|
9880
6553
|
label: options.label ?? "Loja",
|
|
9881
6554
|
icon: "Store",
|
|
9882
|
-
component:
|
|
6555
|
+
component: ProductListingTabView,
|
|
9883
6556
|
props: { getProvider: options.provider }
|
|
9884
6557
|
};
|
|
9885
6558
|
}
|
|
9886
6559
|
|
|
9887
|
-
export { ABACATEPAY_CONNECTOR_ID, ABACATEPAY_LATENCY_BUDGET_MS, APPMAX_CONNECTOR_ID, APPMAX_LATENCY_BUDGET_MS, BLING_CONNECTOR_ID, BLING_MARKETPLACE_SOURCE, BLING_MAX_WINDOW_MS, BLING_PAGE_SIZE, BLING_RATE_LIMIT, BLING_SAMPLE_SIZE, BLING_SIGNATURE_HEADER, BLING_TOKEN_LEAD_MS, BLING_WEBHOOK_DEADLINE_MS, BLING_WINDOW_OVERLAP_MS, MELHOR_ENVIO_CONNECTOR_ID, MELHOR_ENVIO_LATENCY_BUDGET_MS, MERCADOPAGO_CONNECTOR_ID, MERCADOPAGO_LATENCY_BUDGET_MS, NEOCHEF_CONNECTOR_ID, NEOCHEF_CURSOR_OVERLAP_MS, NEOCHEF_FUNCTION, NEOCHEF_MARK, NEOCHEF_PAGE_SIZE, NEOCHEF_RATE_LIMIT, NEOCHEF_SAMPLE_SIZE, NEOCHEF_TOKEN_LEAD_MS,
|
|
6560
|
+
export { ABACATEPAY_CONNECTOR_ID, ABACATEPAY_LATENCY_BUDGET_MS, APPMAX_CONNECTOR_ID, APPMAX_LATENCY_BUDGET_MS, BLING_CONNECTOR_ID, BLING_MARKETPLACE_SOURCE, BLING_MAX_WINDOW_MS, BLING_PAGE_SIZE, BLING_RATE_LIMIT, BLING_SAMPLE_SIZE, BLING_SIGNATURE_HEADER, BLING_TOKEN_LEAD_MS, BLING_WEBHOOK_DEADLINE_MS, BLING_WINDOW_OVERLAP_MS, MELHOR_ENVIO_CONNECTOR_ID, MELHOR_ENVIO_LATENCY_BUDGET_MS, MERCADOPAGO_CONNECTOR_ID, MERCADOPAGO_LATENCY_BUDGET_MS, NEOCHEF_CONNECTOR_ID, NEOCHEF_CURSOR_OVERLAP_MS, NEOCHEF_FUNCTION, NEOCHEF_MARK, NEOCHEF_PAGE_SIZE, NEOCHEF_RATE_LIMIT, NEOCHEF_SAMPLE_SIZE, NEOCHEF_TOKEN_LEAD_MS, STRIPE_CONNECT_CONNECTOR_ID, STRIPE_CONNECT_LATENCY_BUDGET_MS, VINDI_CONNECTOR_ID, VINDI_LATENCY_BUDGET_MS, abacatePayConnectorDef, acceptWebhook, advanceNeoChefCursor, appmaxConnectorDef, blingConnectionStore, blingConnectorDef, buildShopAnalyticsCards, createShopPlugin, firstWindow, listOrderDeliveries2 as listOrderDeliveries, melhorEnvioConnectorDef, mercadoPagoConnectorDef, neoChefConnectionStore, neoChefConnectorDef, orphanConnection, pendingOrders2 as pendingOrders, pullCatalogue2 as pullCatalogue, pullProducts, pushOrders2 as pushOrders, reconcile2 as reconcile, routeCallback, sampleProducts, shopProductContentTab, shopProductListingTab, shopProductMarketPricesTab, stripeConnectConnectorDef, testNeoChef, toMirroredNeoChefProduct, toMirroredProduct, toNeoChefOrder, vindiConnectorDef, windowFor };
|
|
9888
6561
|
//# sourceMappingURL=index.js.map
|
|
9889
6562
|
//# sourceMappingURL=index.js.map
|