@fayz-ai/plugin-shop 0.24.5 → 0.24.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/CheckoutSettings-GQRYZF6I.js +128 -0
  2. package/dist/CheckoutSettings-GQRYZF6I.js.map +1 -0
  3. package/dist/ContentPage-2PHOZSFZ.js +510 -0
  4. package/dist/ContentPage-2PHOZSFZ.js.map +1 -0
  5. package/dist/DeliveryStoreSettings-L2RXOVB6.js +172 -0
  6. package/dist/DeliveryStoreSettings-L2RXOVB6.js.map +1 -0
  7. package/dist/FulfillmentSettings-PW233UOK.js +61 -0
  8. package/dist/FulfillmentSettings-PW233UOK.js.map +1 -0
  9. package/dist/MarketsSettings-YUEVDLIR.js +19 -0
  10. package/dist/MarketsSettings-YUEVDLIR.js.map +1 -0
  11. package/dist/PaymentsSettings-5CI74TY2.js +318 -0
  12. package/dist/PaymentsSettings-5CI74TY2.js.map +1 -0
  13. package/dist/ProductContentTab-PSNDS7JZ.js +4 -0
  14. package/dist/ProductContentTab-PSNDS7JZ.js.map +1 -0
  15. package/dist/ProductListingTab-55Y5RXU7.js +4 -0
  16. package/dist/ProductListingTab-55Y5RXU7.js.map +1 -0
  17. package/dist/ProductMarketPricesTab-QD2ZXEWL.js +4 -0
  18. package/dist/ProductMarketPricesTab-QD2ZXEWL.js.map +1 -0
  19. package/dist/ShippingSettings-UBTNRBJ4.js +9 -0
  20. package/dist/ShippingSettings-UBTNRBJ4.js.map +1 -0
  21. package/dist/{ShopPage-NUIIWBCB.js → ShopPage-OUV3FITM.js} +21 -7
  22. package/dist/ShopPage-OUV3FITM.js.map +1 -0
  23. package/dist/StoreGeneralSettings-DEZ2CMVG.js +97 -0
  24. package/dist/StoreGeneralSettings-DEZ2CMVG.js.map +1 -0
  25. package/dist/chunk-2MXOKJQO.js +23 -0
  26. package/dist/chunk-2MXOKJQO.js.map +1 -0
  27. package/dist/chunk-4JM32Z7J.js +51 -0
  28. package/dist/chunk-4JM32Z7J.js.map +1 -0
  29. package/dist/chunk-53Y6VLAW.js +132 -0
  30. package/dist/chunk-53Y6VLAW.js.map +1 -0
  31. package/dist/chunk-6V2VAUWP.js +24 -0
  32. package/dist/chunk-6V2VAUWP.js.map +1 -0
  33. package/dist/chunk-FQQMEM26.js +59 -0
  34. package/dist/chunk-FQQMEM26.js.map +1 -0
  35. package/dist/chunk-JYT6BTRE.js +298 -0
  36. package/dist/chunk-JYT6BTRE.js.map +1 -0
  37. package/dist/chunk-MFEOIJI7.js +127 -0
  38. package/dist/chunk-MFEOIJI7.js.map +1 -0
  39. package/dist/chunk-R5CUDFOI.js +61 -0
  40. package/dist/chunk-R5CUDFOI.js.map +1 -0
  41. package/dist/chunk-STHGZWBQ.js +1455 -0
  42. package/dist/chunk-STHGZWBQ.js.map +1 -0
  43. package/dist/{chunk-XV3KZRQX.js → chunk-STZYH32S.js} +9 -140
  44. package/dist/chunk-STZYH32S.js.map +1 -0
  45. package/dist/chunk-TDV3SFXT.js +75 -0
  46. package/dist/chunk-TDV3SFXT.js.map +1 -0
  47. package/dist/chunk-YUG6XQHJ.js +55 -0
  48. package/dist/chunk-YUG6XQHJ.js.map +1 -0
  49. package/dist/data/registry-routes.d.ts.map +1 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +129 -3456
  52. package/dist/index.js.map +1 -1
  53. package/dist/settings/ShippingSettings.d.ts.map +1 -1
  54. package/dist/views/EmailTemplatePage.d.ts.map +1 -1
  55. package/dist/views/EmailsPage.d.ts.map +1 -1
  56. package/dist/views/OrderDeliveryPanel.d.ts +3 -1
  57. package/dist/views/OrderDeliveryPanel.d.ts.map +1 -1
  58. package/dist/views/OrderDetailView.d.ts.map +1 -1
  59. package/dist/views/ProductContentTab.d.ts.map +1 -1
  60. package/dist/views/ProductMarketPricesTab.d.ts.map +1 -1
  61. package/dist/views/media-links.d.ts.map +1 -1
  62. package/package.json +7 -7
  63. package/dist/ShopPage-NUIIWBCB.js.map +0 -1
  64. package/dist/chunk-XV3KZRQX.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,11 +1,18 @@
1
- import { errorMessage, shopCustomerLookup, resolve, invalidateProductLists, listProductsDetailed, applyQuery, buildShopRegistries, locationSummary, LOCATIONS_REGISTRY, SHIPPING_ZONES_REGISTRY, listProductsCached, resolveTerm, candidateMeta, SHIPPING_PROFILES_REGISTRY, packageMeasureLabel, packagesRegistry, PACKAGES_REGISTRY, marketsRegistry } from './chunk-XV3KZRQX.js';
2
- import * as React14 from 'react';
3
- import React14__default, { useState, useEffect, useMemo, useRef, useCallback } from 'react';
4
- import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, tl, getActiveTenantId, getSupabaseClientOptional, registerMessageDefaults, registerComponent, formatCurrency, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, countByTenant, describeError, useActiveUnitId, errorMessage as errorMessage$1, emitDataChanged, resolveDataProvider } from '@fayz-ai/core';
5
- import { getShopTenantId, setShopTenantResolver, buildShopMessageDefaults, registerShippingLabelProvider, getShopProvider, buildShopDomainEvents, axesFromOptions, planVariantMatrix, validateVariantAxes, hasOnlyDefaultVariant, MAX_VARIANT_AXES, leavesNothingSellable, productImageToMediaFile, formatVariantCombination, applyVariantMatrix } from '@fayz-ai/shop';
6
- import { fayzConnectorOAuth, describeConnectorAccount, useOrganizationStore, registerEntityPath, registerPersonLookup, createCrudPage, registerCrudNavTabs, adminNavigateTo, DetailTabHeader, AskAIButton, PageActions, SettingsGroup, useAgentSurface, askAI, useSettingKeys, TextRow, useConnectors, useConnectorsPanelAvailable, openConnectorsPanel, RegistryCrudView, ConnectorRow, ConnectorDetail, CrudFormPage, SelectRow, ToggleRow, MultiSelectRow, AddConnectorButton, AddConnectorPanel, ConnectorMark, StateChip } from '@fayz-ai/admin';
7
- import { Package, Eye, FileEdit, PackageX, Users, UsersRound, Filter, UserX, Tag, TicketCheck, Layers, CreditCard, CircleDollarSign, Plus, UploadCloud, Upload, ImageOff, Loader2, WandSparkles, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, ArrowRight, AlertTriangle, Warehouse, MapPin, Car, Truck, X, ExternalLink, Box, ChevronDown, Film, FileText, Sparkles, Ban, ListChecks, Check, Search, Building2, Info } from 'lucide-react';
8
- import { Skeleton, Badge, CurrencyInput, Button, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, PageTabs, PageHeaderActions, Card, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, Input, ModalFooter, Thumbnail, PageHeader, toast, ConfirmDialog, Checkbox, Hint, DataTable, ModalBody } from '@fayz-ai/ui';
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(resolve3) {
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 resolve3().quoteShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? unsupported(),
205
- buy: async (orderId, serviceId) => await resolve3().buyShippingLabel?.(orderId, serviceId, MELHOR_ENVIO_CARRIER) ?? unsupported(),
206
- print: async (orderId) => await resolve3().printShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? null,
207
- track: async (orderId) => await resolve3().trackShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? null
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] = React14__default.useState([]);
444
- const [variants, setVariants] = React14__default.useState([]);
445
- const [busy, setBusy] = React14__default.useState(false);
446
- const [adding, setAdding] = React14__default.useState(false);
447
- const [error, setError] = React14__default.useState(null);
448
- const [selected, setSelected] = React14__default.useState(/* @__PURE__ */ new Set());
449
- const [dirty, setDirty] = React14__default.useState(false);
450
- const reload = React14__default.useCallback(async () => {
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
- React14__default.useEffect(() => {
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 = React14__default.useMemo(() => planVariantMatrix(axes, variants), [axes, variants]);
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 [draft2, setDraft] = React14__default.useState("");
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(IconButton2, { label: "Mover para cima", disabled: disabled || index === 0, onClick: () => onMove(-1), children: "\u2191" }),
617
- /* @__PURE__ */ jsx(IconButton2, { label: "Mover para baixo", disabled: disabled || index === count - 1, onClick: () => onMove(1), children: "\u2193" }),
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: draft2,
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(draft2);
436
+ commit(draft);
659
437
  }
660
438
  },
661
- onBlur: () => commit(draft2),
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] = React14__default.useState("");
877
- const [weight, setWeight] = React14__default.useState("");
878
- const [unit, setUnit] = React14__default.useState("");
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 [draft2, setDraft] = React14__default.useState(value == null ? "" : String(value));
956
- React14__default.useEffect(() => {
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: draft2,
740
+ value: draft,
963
741
  disabled,
964
742
  placeholder,
965
743
  onChange: (e) => setDraft(e.target.value),
966
744
  onBlur: () => {
967
- const parsed = parseNumber(draft2);
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 [draft2, setDraft] = React14__default.useState(value);
976
- React14__default.useEffect(() => {
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: draft2,
760
+ value: draft,
983
761
  disabled,
984
762
  placeholder,
985
763
  onChange: (e) => setDraft(e.target.value),
986
- onBlur: () => onCommit(draft2.trim()),
764
+ onBlur: () => onCommit(draft.trim()),
987
765
  className: COMPACT_INPUT
988
766
  }
989
767
  ) });
990
768
  }
991
- function IconButton2({ label, disabled, onClick, children }) {
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] = React14__default.useState("");
1027
- const [values, setValues] = React14__default.useState([""]);
1028
- React14__default.useEffect(() => {
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 = React14__default.useRef(null);
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 = React14__default.useRef({ product, variantId, total, getProvider, load });
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 = React14__default.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
1770
- const [rows, setRows] = React14__default.useState(null);
1771
- const [open, setOpen] = React14__default.useState(false);
1772
- const [creating, setCreating] = React14__default.useState(false);
1773
- const ref = React14__default.useRef(null);
1774
- const load = React14__default.useCallback(() => {
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
- React14__default.useEffect(load, [load]);
1782
- React14__default.useEffect(() => {
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] = React14__default.useState(null);
1884
- const provider = React14__default.useMemo(
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 = React14__default.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
1934
- const [rows, setRows] = React14__default.useState(null);
1935
- React14__default.useEffect(() => {
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 money3 = (v) => formatCurrency(Number(v) || 0, currencyCode);
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: money3(v) })
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: money3(v) })
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, money3) {
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 ${money3(s.minSpent)}\u2013${money3(s.maxSpent)}`);
2927
- else if (s.minSpent != null) parts.push(`gastou ${money3(s.minSpent)}+`);
2928
- else if (s.maxSpent != null) parts.push(`gastou at\xE9 ${money3(s.maxSpent)}`);
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 money3 = (v) => formatCurrency(v, currencyCode);
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, money3) })
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 = React14__default.lazy(() => import('./ShopPage-NUIIWBCB.js').then((m) => ({ default: m.ShopPage })));
9328
- var LazyOrderDetail = React14__default.lazy(() => import('./ShopPage-NUIIWBCB.js').then((m) => ({ default: m.ShopOrderDetailSlot })));
5989
+ var ShopPage = React.lazy(() => import('./ShopPage-OUV3FITM.js').then((m) => ({ default: m.ShopPage })));
5990
+ var LazyOrderDetail = React.lazy(() => import('./ShopPage-OUV3FITM.js').then((m) => ({ default: m.ShopOrderDetailSlot })));
9329
5991
  function ShopOrderDetail(provider) {
9330
- const Detail = (props) => React14__default.createElement(
9331
- React14__default.Suspense,
5992
+ const Detail = (props) => React.createElement(
5993
+ React.Suspense,
9332
5994
  { fallback: null },
9333
- React14__default.createElement(LazyOrderDetail, { ...props, provider })
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 = () => React14__default.createElement(
9347
- React14__default.Suspense,
6008
+ const Wrapped = () => React.createElement(
6009
+ React.Suspense,
9348
6010
  { fallback: null },
9349
- React14__default.createElement(
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 money3 = (v) => formatCurrency(v, curr.code);
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: money3(active.reduce((sum, g) => sum + (g.balance ?? 0), 0))
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: money3(items?.reduce((sum, g) => sum + (g.initialBalance ?? 0), 0) ?? 0)
6195
+ value: money2(items?.reduce((sum, g) => sum + (g.initialBalance ?? 0), 0) ?? 0)
9534
6196
  }
9535
6197
  ];
9536
6198
  }
9537
6199
  });
9538
- const ContentPageWrapped = () => React14__default.createElement(ContentPage, { resolveProvider: options?.provider });
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
- React14__default.useEffect(() => {
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 = () => React14__default.createElement(DeliveryStoreSettings, { provider: options?.provider });
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", () => React14__default.createElement(StoreGeneralSettings, { currency: curr, provider: options?.provider })),
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", () => React14__default.createElement(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", () => React14__default.createElement(PaymentsSettings, { provider: options?.provider })),
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", () => React14__default.createElement(CheckoutSettings, { provider: options?.provider })),
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", () => React14__default.createElement(ShippingSettings, { provider: options?.provider })),
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", () => React14__default.createElement(FulfillmentSettings, { provider: options?.provider })),
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: ProductContentTab,
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: ProductMarketPricesTab,
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: ProductListingTab,
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, ProductContentTab, ProductListingTab, ProductMarketPricesTab, 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 };
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