@hszrdm/core 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1788,283 +1788,400 @@ const SystemMonitoringPage = () => /* @__PURE__ */ jsxRuntime.jsxs(ui.Container,
1788
1788
  ] }, item.href)) })
1789
1789
  ] });
1790
1790
  const config$9 = adminSdk.defineRouteConfig({ label: "Rendszerfelügyelet" });
1791
- const defaults$1 = {
1792
- ARUKERESO_STOREFRONT_URL: "",
1793
- ARUKERESO_PRODUCT_PATH_TEMPLATE: "/{country_code}/products/{handle}?v_id={variant_id}",
1794
- ARUKERESO_SALES_CHANNEL_ID: "",
1795
- ARUKERESO_DEFAULT_MANUFACTURER: "",
1796
- ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER: false,
1797
- ARUKERESO_INCLUDE_OUT_OF_STOCK: true,
1798
- ARUKERESO_DEFAULT_DELIVERY_TIME: "",
1799
- ARUKERESO_DEFAULT_DELIVERY_COST: ""
1800
- };
1801
1791
  const backendUrl$a = (__BACKEND_URL__ || "").replace(/\/+$/, "");
1802
- const apiUrl$a = (path) => `${backendUrl$a}${path.startsWith("/") ? path : `/${path}`}`;
1803
- const ArukeresoPage = () => {
1804
- const [enabled, setEnabled] = React.useState(false);
1805
- const [draft, setDraft] = React.useState(defaults$1);
1806
- const [loading, setLoading] = React.useState(true);
1807
- const [saving, setSaving] = React.useState(false);
1808
- const [previewing, setPreviewing] = React.useState(false);
1809
- const [preview, setPreview] = React.useState(null);
1810
- const update = (key, value) => {
1811
- setDraft((current) => ({ ...current, [key]: value }));
1812
- };
1792
+ const adminBase$5 = (__BASE__ || "/app").replace(/\/+$/, "");
1793
+ function apiUrl$a(path) {
1794
+ return `${backendUrl$a}${path.startsWith("/") ? path : `/${path}`}`;
1795
+ }
1796
+ const BillingPage = () => {
1797
+ const [orders, setOrders] = React.useState([]);
1798
+ const [configuration, setConfiguration] = React.useState(null);
1799
+ const [selected, setSelected] = React.useState([]);
1800
+ const [query, setQuery] = React.useState("");
1801
+ const [loading, setLoading] = React.useState(null);
1802
+ const [listLoading, setListLoading] = React.useState(true);
1803
+ const [loadError, setLoadError] = React.useState(null);
1813
1804
  const load = React.useCallback(async () => {
1814
- setLoading(true);
1805
+ setListLoading(true);
1806
+ setLoadError(null);
1815
1807
  try {
1816
- const response = await fetch(apiUrl$a("/admin/integrations/arukereso"), {
1808
+ const response = await fetch(apiUrl$a("/admin/billingo/orders"), {
1817
1809
  credentials: "include"
1818
1810
  });
1819
- const data = await response.json().catch(() => ({}));
1820
- if (!response.ok || !data.integration) {
1821
- throw new Error(data.message || "Az Árukereső beállítások nem tölthetők be.");
1811
+ const payload = await response.json().catch(() => ({}));
1812
+ if (!response.ok) {
1813
+ throw new Error(payload.message || "A számlázási lista nem tölthető be.");
1822
1814
  }
1823
- const values = data.integration.values || {};
1824
- setEnabled(Boolean(data.integration.enabled));
1825
- setDraft({
1826
- ARUKERESO_STOREFRONT_URL: values.ARUKERESO_STOREFRONT_URL || "",
1827
- ARUKERESO_PRODUCT_PATH_TEMPLATE: values.ARUKERESO_PRODUCT_PATH_TEMPLATE || "/{country_code}/products/{handle}?v_id={variant_id}",
1828
- ARUKERESO_SALES_CHANNEL_ID: values.ARUKERESO_SALES_CHANNEL_ID || "",
1829
- ARUKERESO_DEFAULT_MANUFACTURER: values.ARUKERESO_DEFAULT_MANUFACTURER || "",
1830
- ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER: values.ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER === "true",
1831
- ARUKERESO_INCLUDE_OUT_OF_STOCK: values.ARUKERESO_INCLUDE_OUT_OF_STOCK !== "false",
1832
- ARUKERESO_DEFAULT_DELIVERY_TIME: values.ARUKERESO_DEFAULT_DELIVERY_TIME || "",
1833
- ARUKERESO_DEFAULT_DELIVERY_COST: values.ARUKERESO_DEFAULT_DELIVERY_COST || ""
1834
- });
1815
+ const nextOrders = payload.orders || [];
1816
+ setOrders(nextOrders);
1817
+ setConfiguration(payload.configuration || null);
1818
+ setSelected(
1819
+ (current) => current.filter((id) => nextOrders.some((order) => order.id === id))
1820
+ );
1835
1821
  } catch (error) {
1836
- ui.toast.error(error instanceof Error ? error.message : "Betöltési hiba.");
1822
+ setOrders([]);
1823
+ setLoadError(
1824
+ error instanceof Error ? error.message : "A számlázási lista nem tölthető be."
1825
+ );
1837
1826
  } finally {
1838
- setLoading(false);
1827
+ setListLoading(false);
1839
1828
  }
1840
1829
  }, []);
1841
1830
  React.useEffect(() => {
1842
1831
  void load();
1843
1832
  }, [load]);
1844
- const save = async () => {
1845
- setSaving(true);
1833
+ const visible = React.useMemo(() => {
1834
+ const value = query.trim().toLowerCase();
1835
+ if (!value) return orders;
1836
+ return orders.filter(
1837
+ (order) => [
1838
+ order.display_id,
1839
+ order.email,
1840
+ order.invoice?.invoiceNumber,
1841
+ order.invoice?.modificationInvoiceNumber
1842
+ ].some(
1843
+ (item) => String(item || "").toLowerCase().includes(value)
1844
+ )
1845
+ );
1846
+ }, [orders, query]);
1847
+ const selectedSet = React.useMemo(() => new Set(selected), [selected]);
1848
+ const toggle = (id) => {
1849
+ setSelected(
1850
+ (current) => current.includes(id) ? current.filter((item) => item !== id) : [...current, id]
1851
+ );
1852
+ };
1853
+ const bulkCreate = async () => {
1854
+ if (!selected.length) {
1855
+ ui.toast.error("Jelölj ki legalább egy rendelést.");
1856
+ return;
1857
+ }
1858
+ setLoading("bulk");
1846
1859
  try {
1847
- const response = await fetch(apiUrl$a("/admin/integrations/arukereso"), {
1860
+ const response = await fetch(apiUrl$a("/admin/billingo/bulk/create"), {
1848
1861
  method: "POST",
1849
1862
  credentials: "include",
1850
1863
  headers: { "Content-Type": "application/json" },
1851
- body: JSON.stringify({ enabled, values: draft })
1864
+ body: JSON.stringify({ orderIds: selected })
1852
1865
  });
1853
- const data = await response.json().catch(() => ({}));
1854
- if (!response.ok) throw new Error(data.message || "A mentés nem sikerült.");
1855
- ui.toast.success(data.message || "Árukereső beállítások mentve.");
1856
- setPreview(null);
1866
+ const payload = await response.json().catch(() => ({}));
1867
+ if (!response.ok && response.status !== 207) {
1868
+ throw new Error(payload.message || "A tömeges számlázás nem sikerült.");
1869
+ }
1870
+ const failed = payload.results?.filter((item) => !item.ok).length || 0;
1871
+ const created = payload.results?.filter(
1872
+ (item) => item.ok && !item.alreadyExisted
1873
+ ).length || 0;
1874
+ if (failed) {
1875
+ ui.toast.error(`${failed} számla nem készült el; ${created} sikeres.`);
1876
+ } else {
1877
+ ui.toast.success(`${created} új számla elkészült.`);
1878
+ }
1857
1879
  await load();
1858
1880
  } catch (error) {
1859
- ui.toast.error(error instanceof Error ? error.message : "A mentés nem sikerült.");
1881
+ ui.toast.error(
1882
+ error instanceof Error ? error.message : "A tömeges számlázás nem sikerült."
1883
+ );
1860
1884
  } finally {
1861
- setSaving(false);
1885
+ setLoading(null);
1862
1886
  }
1863
1887
  };
1864
- const runPreview = async () => {
1865
- setPreviewing(true);
1888
+ const action = async (order, value) => {
1889
+ if (value === "cancel" && !confirm(
1890
+ `Biztosan sztornózod a(z) ${order.invoice?.invoiceNumber || order.invoice?.documentId} számlát?`
1891
+ )) {
1892
+ return;
1893
+ }
1894
+ if (value === "modify" && !confirm(
1895
+ `Biztosan teljes jóváíró/módosító bizonylatot készítesz a(z) ${order.invoice?.invoiceNumber || order.invoice?.documentId} számlához? Ez a teljes számla összegét jóváírja.`
1896
+ )) {
1897
+ return;
1898
+ }
1899
+ setLoading(`${order.id}:${value}`);
1900
+ try {
1901
+ const response = await fetch(apiUrl$a(`/admin/orders/${order.id}/billingo`), {
1902
+ method: "POST",
1903
+ credentials: "include",
1904
+ headers: { "Content-Type": "application/json" },
1905
+ body: JSON.stringify({ action: value })
1906
+ });
1907
+ const payload = await response.json().catch(() => ({}));
1908
+ if (!response.ok) {
1909
+ throw new Error(payload.message || "A Billingo művelet nem sikerült.");
1910
+ }
1911
+ const messages = {
1912
+ create: "A számla elkészült.",
1913
+ sync: "A számla frissítve.",
1914
+ send: "A számla elküldve.",
1915
+ cancel: "A számla sztornózva.",
1916
+ modify: "A teljes jóváíró/módosító bizonylat elkészült."
1917
+ };
1918
+ ui.toast.success(messages[value]);
1919
+ if (value === "modify" && payload.sendError) {
1920
+ ui.toast.error(`A jóváírás elkészült, de az e-mail küldése sikertelen: ${payload.sendError}`);
1921
+ }
1922
+ await load();
1923
+ } catch (error) {
1924
+ ui.toast.error(
1925
+ error instanceof Error ? error.message : "A Billingo művelet nem sikerült."
1926
+ );
1927
+ } finally {
1928
+ setLoading(null);
1929
+ }
1930
+ };
1931
+ const testConnection = async () => {
1932
+ setLoading("connection");
1866
1933
  try {
1867
- const response = await fetch(apiUrl$a("/admin/arukereso/preview"), {
1934
+ const response = await fetch(apiUrl$a("/admin/billingo/connection"), {
1935
+ method: "POST",
1868
1936
  credentials: "include"
1869
1937
  });
1870
- const data = await response.json().catch(() => ({}));
1871
- if (!response.ok) throw new Error(data.message || "A feed nem generálható.");
1872
- setPreview(data);
1873
- ui.toast.success("Az Árukereső feed ellenőrzése elkészült.");
1938
+ const payload = await response.json().catch(() => ({}));
1939
+ if (!response.ok) {
1940
+ throw new Error(payload.message || "A Billingo kapcsolat nem működik.");
1941
+ }
1942
+ ui.toast.success(payload.message || "A Billingo kapcsolat működik.");
1874
1943
  } catch (error) {
1875
- ui.toast.error(error instanceof Error ? error.message : "A feed nem generálható.");
1944
+ ui.toast.error(
1945
+ error instanceof Error ? error.message : "A Billingo kapcsolat nem működik."
1946
+ );
1876
1947
  } finally {
1877
- setPreviewing(false);
1948
+ setLoading(null);
1878
1949
  }
1879
1950
  };
1880
- const feedUrl = apiUrl$a("/feeds/arukereso.xml");
1881
- const d = preview?.diagnostics;
1882
1951
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
1883
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4 border-b px-6 py-4", children: [
1952
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 border-b px-6 py-4", children: [
1884
1953
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1885
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Árukereső.hu" }),
1886
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Árukereső XML feed a Medusa termék-, variant-, HU/HUF ár-, ÁFA- és készletadataiból." })
1954
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Számlázás" }),
1955
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Billingo számlák, sztornók és teljes jóváírások kezelése a HSZRDM Commerce rendeléseiből." }),
1956
+ configuration?.autoInvoice ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
1957
+ "Az automatikus számlázás aktív",
1958
+ configuration.autoSend ? ", automatikus e-mail küldéssel" : "",
1959
+ "."
1960
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-warning", children: "Az automatikus számlázás ki van kapcsolva." })
1887
1961
  ] }),
1888
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
1889
- /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: enabled, onCheckedChange: setEnabled, disabled: loading }),
1890
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: enabled ? "Bekapcsolva" : "Kikapcsolva" })
1891
- ] })
1892
- ] }),
1893
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 border-b px-6 py-5 md:grid-cols-2", children: [
1894
- /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "Storefront URL", children: /* @__PURE__ */ jsxRuntime.jsx(
1895
- ui.Input,
1896
- {
1897
- value: draft.ARUKERESO_STOREFRONT_URL,
1898
- onChange: (e) => update("ARUKERESO_STOREFRONT_URL", e.target.value),
1899
- placeholder: "https://shop.hu (üresen STOREFRONT_URL)"
1900
- }
1901
- ) }),
1902
- /* @__PURE__ */ jsxRuntime.jsxs(Field$4, { label: "Termék URL sablon", children: [
1962
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1963
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { children: [
1964
+ selected.length,
1965
+ " kijelölve"
1966
+ ] }),
1903
1967
  /* @__PURE__ */ jsxRuntime.jsx(
1904
- ui.Input,
1968
+ ui.Button,
1905
1969
  {
1906
- value: draft.ARUKERESO_PRODUCT_PATH_TEMPLATE,
1907
- onChange: (e) => update("ARUKERESO_PRODUCT_PATH_TEMPLATE", e.target.value)
1970
+ variant: "secondary",
1971
+ isLoading: loading === "connection",
1972
+ onClick: () => void testConnection(),
1973
+ children: "Kapcsolat tesztelése"
1908
1974
  }
1909
- ),
1910
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-xs", children: [
1911
- "Helyőrzők: ",
1912
- "{country_code}",
1913
- ", ",
1914
- "{currency_code}",
1915
- ", ",
1916
- "{handle}",
1917
- ", ",
1918
- "{variant_id}",
1919
- "."
1920
- ] })
1921
- ] }),
1922
- /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "Sales Channel ID", children: /* @__PURE__ */ jsxRuntime.jsx(
1975
+ )
1976
+ ] })
1977
+ ] }),
1978
+ configuration && !configuration.configured ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-ui-border-error bg-ui-bg-base px-6 py-4", children: [
1979
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-error", children: "A Billingo még nincs teljesen beállítva." }),
1980
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-subtle", children: [
1981
+ "Hiányzik: ",
1982
+ [
1983
+ !configuration.apiKey && "API kulcs",
1984
+ !configuration.documentBlockId && "számlatömb azonosító"
1985
+ ].filter(Boolean).join(", "),
1986
+ ". Állítsd be a Settings → Integrációk → Billingo oldalon, majd indítsd újra a backendet."
1987
+ ] })
1988
+ ] }) : null,
1989
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3 border-b px-6 py-4", children: [
1990
+ /* @__PURE__ */ jsxRuntime.jsx(
1923
1991
  ui.Input,
1924
1992
  {
1925
- value: draft.ARUKERESO_SALES_CHANNEL_ID,
1926
- onChange: (e) => update("ARUKERESO_SALES_CHANNEL_ID", e.target.value),
1927
- placeholder: "sc_... (opcionális)"
1993
+ className: "max-w-sm",
1994
+ value: query,
1995
+ onChange: (event) => setQuery(event.target.value),
1996
+ placeholder: "Rendelés, e-mail vagy számlaszám keresése"
1928
1997
  }
1929
- ) }),
1930
- /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "Alapértelmezett gyártó", children: /* @__PURE__ */ jsxRuntime.jsx(
1931
- ui.Input,
1932
- {
1933
- value: draft.ARUKERESO_DEFAULT_MANUFACTURER,
1934
- onChange: (e) => update("ARUKERESO_DEFAULT_MANUFACTURER", e.target.value),
1935
- placeholder: "Csak ha minden termékre tényleg igaz"
1936
- }
1937
- ) }),
1938
- /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "SKU mint gyártói cikkszám", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center gap-2", children: [
1939
- /* @__PURE__ */ jsxRuntime.jsx(
1940
- ui.Switch,
1941
- {
1942
- checked: draft.ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER,
1943
- onCheckedChange: (value) => update("ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER", value)
1944
- }
1945
- ),
1946
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: draft.ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER ? "Igen" : "Nem" })
1947
- ] }) }),
1948
- /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "Készlethiányos termékek", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center gap-2", children: [
1949
- /* @__PURE__ */ jsxRuntime.jsx(
1950
- ui.Switch,
1951
- {
1952
- checked: draft.ARUKERESO_INCLUDE_OUT_OF_STOCK,
1953
- onCheckedChange: (value) => update("ARUKERESO_INCLUDE_OUT_OF_STOCK", value)
1954
- }
1955
- ),
1956
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: draft.ARUKERESO_INCLUDE_OUT_OF_STOCK ? "DeliveryTime=NO" : "Kimaradnak" })
1957
- ] }) }),
1958
- /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "Alap szállítási idő", children: /* @__PURE__ */ jsxRuntime.jsx(
1959
- ui.Input,
1998
+ ),
1999
+ /* @__PURE__ */ jsxRuntime.jsx(
2000
+ ui.Button,
1960
2001
  {
1961
- value: draft.ARUKERESO_DEFAULT_DELIVERY_TIME,
1962
- onChange: (e) => update("ARUKERESO_DEFAULT_DELIVERY_TIME", e.target.value),
1963
- placeholder: "1 vagy 3"
2002
+ disabled: Boolean(loading) || !configuration?.configured,
2003
+ isLoading: loading === "bulk",
2004
+ onClick: () => void bulkCreate(),
2005
+ children: "Kijelöltek számlázása"
1964
2006
  }
1965
- ) }),
1966
- /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "Alap szállítási költség", children: /* @__PURE__ */ jsxRuntime.jsx(
1967
- ui.Input,
2007
+ ),
2008
+ /* @__PURE__ */ jsxRuntime.jsx(
2009
+ ui.Button,
1968
2010
  {
1969
- value: draft.ARUKERESO_DEFAULT_DELIVERY_COST,
1970
- onChange: (e) => update("ARUKERESO_DEFAULT_DELIVERY_COST", e.target.value),
1971
- placeholder: "1990 vagy FREE"
2011
+ variant: "secondary",
2012
+ disabled: Boolean(loading),
2013
+ onClick: () => void load(),
2014
+ children: "Frissítés"
1972
2015
  }
1973
- ) })
1974
- ] }),
1975
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b px-6 py-4", children: [
1976
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1977
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: "Publikus feed URL" }),
1978
- /* @__PURE__ */ jsxRuntime.jsx("a", { className: "break-all text-ui-fg-interactive hover:underline", href: feedUrl, target: "_blank", rel: "noreferrer", children: feedUrl })
1979
- ] }),
1980
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
1981
- /* @__PURE__ */ jsxRuntime.jsx(
1982
- ui.Button,
1983
- {
1984
- variant: "secondary",
1985
- isLoading: previewing,
1986
- disabled: !enabled || loading || saving || previewing,
1987
- onClick: () => void runPreview(),
1988
- children: "Feed ellenőrzése"
1989
- }
1990
- ),
1991
- /* @__PURE__ */ jsxRuntime.jsx(
1992
- ui.Button,
1993
- {
1994
- isLoading: saving,
1995
- disabled: loading || saving || previewing,
1996
- onClick: () => void save(),
1997
- children: "Mentés"
1998
- }
1999
- )
2000
- ] })
2016
+ )
2001
2017
  ] }),
2002
- d ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2003
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 border-b px-6 py-5 sm:grid-cols-2 lg:grid-cols-4", children: [
2004
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Termékek", value: d.productsScanned }),
2005
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Variantok", value: d.variantsScanned }),
2006
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Feed tételek", value: d.itemsEmitted }),
2007
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs gyártó", value: d.skippedNoManufacturer }),
2008
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs kategória", value: d.skippedNoCategory }),
2009
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs ár", value: d.skippedNoPrice }),
2010
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs ProductNumber", value: d.itemsMissingProductNumber }),
2011
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs kép", value: d.itemsMissingImage }),
2012
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Készlethiány miatt kimaradt", value: d.skippedOutOfStock }),
2013
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Sales channel hiba", value: d.skippedNoSalesChannel + d.skippedNotInSalesChannel }),
2014
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Hibás delivery time", value: d.invalidDeliveryTime }),
2015
- /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Hibás delivery cost", value: d.invalidDeliveryCost })
2016
- ] }),
2017
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-left text-sm", children: [
2018
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-x-auto", children: [
2019
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-left text-sm", children: [
2018
2020
  /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "border-b bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
2019
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Identifier" }),
2020
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Termék" }),
2021
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Gyártó" }),
2022
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Kategória" }),
2023
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Bruttó / nettó" }),
2024
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Szállítás" })
2021
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
2022
+ ui.Checkbox,
2023
+ {
2024
+ checked: visible.length > 0 && visible.every((order) => selectedSet.has(order.id)),
2025
+ onCheckedChange: () => setSelected(
2026
+ visible.every((order) => selectedSet.has(order.id)) ? selected.filter(
2027
+ (id) => !visible.some((order) => order.id === id)
2028
+ ) : [
2029
+ .../* @__PURE__ */ new Set([
2030
+ ...selected,
2031
+ ...visible.map((order) => order.id)
2032
+ ])
2033
+ ]
2034
+ )
2035
+ }
2036
+ ) }),
2037
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Rendelés" }),
2038
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Fizetés" }),
2039
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Számla" }),
2040
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Billingo státusz" }),
2041
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Műveletek" })
2025
2042
  ] }) }),
2026
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: (preview?.sample || []).map((item) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b", children: [
2027
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: item.identifier }) }),
2028
- /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2029
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.productUrl, target: "_blank", rel: "noreferrer", className: "text-ui-fg-interactive hover:underline", children: item.name }),
2030
- item.productNumber ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-muted text-xs", children: [
2031
- "PN: ",
2032
- item.productNumber
2033
- ] }) : null
2034
- ] }),
2035
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.manufacturer }),
2036
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.category }),
2037
- /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2038
- item.price,
2039
- " / ",
2040
- item.netPrice,
2041
- " HUF"
2042
- ] }),
2043
- /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2044
- item.deliveryTime || "—",
2045
- " · ",
2046
- item.deliveryCost || "—"
2047
- ] })
2048
- ] }, item.identifier)) })
2049
- ] }) })
2050
- ] }) : null
2043
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: visible.map((order) => {
2044
+ const invoice = order.invoice;
2045
+ const last = order.operations?.at(-1);
2046
+ const modified = Boolean(invoice?.modificationDocumentId);
2047
+ return /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b align-top", children: [
2048
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
2049
+ ui.Checkbox,
2050
+ {
2051
+ checked: selectedSet.has(order.id),
2052
+ onCheckedChange: () => toggle(order.id)
2053
+ }
2054
+ ) }),
2055
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2056
+ /* @__PURE__ */ jsxRuntime.jsxs(
2057
+ "a",
2058
+ {
2059
+ className: "text-ui-fg-interactive",
2060
+ href: `${adminBase$5}/orders/${order.id}`,
2061
+ children: [
2062
+ "#",
2063
+ order.display_id ?? order.id
2064
+ ]
2065
+ }
2066
+ ),
2067
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: order.email || "—" }),
2068
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: formatMoney(order.total, order.currency_code) })
2069
+ ] }),
2070
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: order.payment_status === "captured" ? "Fizetve" : order.payment_status === "refunded" ? "Visszatérítve" : order.payment_status === "partially_refunded" ? "Részben visszatérítve" : order.payment_status || "—" }),
2071
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2072
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: invoice?.invoiceNumber || (invoice ? `#${invoice.documentId}` : "Még nincs") }),
2073
+ invoice?.cancelled ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-error", children: "Sztornózva" }) : null,
2074
+ modified ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-interactive", children: [
2075
+ "Jóváírva: ",
2076
+ invoice?.modificationInvoiceNumber || `#${invoice?.modificationDocumentId}`
2077
+ ] }) : null,
2078
+ last?.status === "failed" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xs text-ui-fg-error", children: last.message }) : null
2079
+ ] }),
2080
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: invoice ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2081
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: invoice.paymentStatus || "—" }),
2082
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle", children: [
2083
+ "NAV: ",
2084
+ invoice.onlineSzamlaStatus || "—"
2085
+ ] }),
2086
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle", children: [
2087
+ "E-mail: ",
2088
+ invoice.notificationStatus || "—"
2089
+ ] })
2090
+ ] }) : "—" }),
2091
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-w-lg flex-wrap gap-2", children: !invoice ? /* @__PURE__ */ jsxRuntime.jsx(
2092
+ ui.Button,
2093
+ {
2094
+ size: "small",
2095
+ disabled: !configuration?.configured || Boolean(loading),
2096
+ isLoading: loading === `${order.id}:create`,
2097
+ onClick: () => void action(order, "create"),
2098
+ children: "Számla készítése"
2099
+ }
2100
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2101
+ /* @__PURE__ */ jsxRuntime.jsx(
2102
+ ui.Button,
2103
+ {
2104
+ size: "small",
2105
+ variant: "secondary",
2106
+ disabled: Boolean(loading),
2107
+ isLoading: loading === `${order.id}:sync`,
2108
+ onClick: () => void action(order, "sync"),
2109
+ children: "Frissítés"
2110
+ }
2111
+ ),
2112
+ /* @__PURE__ */ jsxRuntime.jsx(
2113
+ "a",
2114
+ {
2115
+ href: apiUrl$a(`/admin/orders/${order.id}/billingo/pdf`),
2116
+ target: "_blank",
2117
+ rel: "noreferrer",
2118
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "PDF" })
2119
+ }
2120
+ ),
2121
+ /* @__PURE__ */ jsxRuntime.jsx(
2122
+ ui.Button,
2123
+ {
2124
+ size: "small",
2125
+ variant: "secondary",
2126
+ disabled: Boolean(loading) || invoice.cancelled,
2127
+ isLoading: loading === `${order.id}:send`,
2128
+ onClick: () => void action(order, "send"),
2129
+ children: "E-mail"
2130
+ }
2131
+ ),
2132
+ /* @__PURE__ */ jsxRuntime.jsx(
2133
+ ui.Button,
2134
+ {
2135
+ size: "small",
2136
+ variant: "secondary",
2137
+ disabled: Boolean(loading) || invoice.cancelled || modified,
2138
+ isLoading: loading === `${order.id}:modify`,
2139
+ onClick: () => void action(order, "modify"),
2140
+ children: "Teljes jóváírás"
2141
+ }
2142
+ ),
2143
+ /* @__PURE__ */ jsxRuntime.jsx(
2144
+ ui.Button,
2145
+ {
2146
+ size: "small",
2147
+ variant: "danger",
2148
+ disabled: Boolean(loading) || invoice.cancelled || modified,
2149
+ isLoading: loading === `${order.id}:cancel`,
2150
+ onClick: () => void action(order, "cancel"),
2151
+ children: "Sztornó"
2152
+ }
2153
+ )
2154
+ ] }) }) })
2155
+ ] }, order.id);
2156
+ }) })
2157
+ ] }),
2158
+ listLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Számlázási rendelések betöltése…" }) }) : null,
2159
+ !listLoading && loadError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "m-6 flex items-center justify-between gap-4 rounded-lg border border-ui-border-error p-4", children: [
2160
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2161
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-error", children: "Betöltési hiba." }),
2162
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: loadError })
2163
+ ] }),
2164
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: () => void load(), children: "Újrapróbálás" })
2165
+ ] }) : null,
2166
+ !listLoading && !loadError && !visible.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Nincs megjeleníthető rendelés." }) }) : null
2167
+ ] })
2051
2168
  ] });
2052
2169
  };
2053
- function Field$4({ label, children }) {
2054
- return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
2055
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: label }),
2056
- children
2057
- ] });
2058
- }
2059
- function Stat$1({ label, value }) {
2060
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-3", children: [
2061
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: label }),
2062
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: value })
2063
- ] });
2170
+ function formatMoney(value, currency) {
2171
+ if (typeof value !== "number") return "—";
2172
+ return new Intl.NumberFormat("hu-HU", {
2173
+ style: "currency",
2174
+ currency: (currency || "HUF").toUpperCase(),
2175
+ maximumFractionDigits: currency?.toUpperCase() === "HUF" ? 0 : 2
2176
+ }).format(value);
2064
2177
  }
2065
- const config$8 = adminSdk.defineRouteConfig({ label: "Árukereső", rank: 42 });
2178
+ const config$8 = adminSdk.defineRouteConfig({
2179
+ label: "Számlázás",
2180
+ icon: Newspaper,
2181
+ rank: 36
2182
+ });
2066
2183
  const backendUrl$9 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
2067
- const adminBase$5 = (__BASE__ || "/app").replace(/\/+$/, "");
2184
+ const adminBase$4 = (__BASE__ || "/app").replace(/\/+$/, "");
2068
2185
  const apiUrl$9 = (path) => `${backendUrl$9}${path.startsWith("/") ? path : `/${path}`}`;
2069
2186
  const LegalPage = () => {
2070
2187
  const [documents, setDocuments] = React.useState([]);
@@ -2219,7 +2336,7 @@ const LegalPage = () => {
2219
2336
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Feltöltött PDF-ek, verziók és checkout-elfogadások. A Fogyasztó Barát ettől külön, opcionális embed." })
2220
2337
  ] }),
2221
2338
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2222
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: `${adminBase$5}/settings/integrations`, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Fogyasztó Barát embed" }) }),
2339
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: `${adminBase$4}/settings/integrations`, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Fogyasztó Barát embed" }) }),
2223
2340
  /* @__PURE__ */ jsxRuntime.jsx(
2224
2341
  ui.Button,
2225
2342
  {
@@ -2240,7 +2357,7 @@ const LegalPage = () => {
2240
2357
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Mindegy, honnan származik a PDF: a Core ugyanúgy verziózza és SHA-256 hash-sel rögzíti. Dokumentumonként külön állítható, hogy checkoutnál kötelező-e elfogadni." })
2241
2358
  ] }),
2242
2359
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 border-b px-6 py-5 md:grid-cols-2", children: [
2243
- /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "Dokumentum neve", children: /* @__PURE__ */ jsxRuntime.jsx(
2360
+ /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "Dokumentum neve", children: /* @__PURE__ */ jsxRuntime.jsx(
2244
2361
  ui.Input,
2245
2362
  {
2246
2363
  value: title,
@@ -2248,7 +2365,7 @@ const LegalPage = () => {
2248
2365
  placeholder: "Általános Szerződési Feltételek"
2249
2366
  }
2250
2367
  ) }),
2251
- /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "Kulcs", children: /* @__PURE__ */ jsxRuntime.jsx(
2368
+ /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "Kulcs", children: /* @__PURE__ */ jsxRuntime.jsx(
2252
2369
  ui.Input,
2253
2370
  {
2254
2371
  value: key,
@@ -2256,7 +2373,7 @@ const LegalPage = () => {
2256
2373
  placeholder: "terms"
2257
2374
  }
2258
2375
  ) }),
2259
- /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "Típus", children: /* @__PURE__ */ jsxRuntime.jsxs(
2376
+ /* @__PURE__ */ jsxRuntime.jsx(Field$4, { label: "Típus", children: /* @__PURE__ */ jsxRuntime.jsxs(
2260
2377
  "select",
2261
2378
  {
2262
2379
  className: "h-8 rounded-md border bg-ui-bg-field px-2 text-sm",
@@ -2270,7 +2387,7 @@ const LegalPage = () => {
2270
2387
  ]
2271
2388
  }
2272
2389
  ) }),
2273
- /* @__PURE__ */ jsxRuntime.jsxs(Field$3, { label: "PDF fájl (max. 5 MB)", children: [
2390
+ /* @__PURE__ */ jsxRuntime.jsxs(Field$4, { label: "PDF fájl (max. 5 MB)", children: [
2274
2391
  /* @__PURE__ */ jsxRuntime.jsx(
2275
2392
  "input",
2276
2393
  {
@@ -2411,7 +2528,7 @@ const LegalPage = () => {
2411
2528
  "a",
2412
2529
  {
2413
2530
  className: "text-ui-fg-interactive",
2414
- href: `${adminBase$5}/orders/${consent.orderId}`,
2531
+ href: `${adminBase$4}/orders/${consent.orderId}`,
2415
2532
  children: consent.orderId
2416
2533
  }
2417
2534
  ) : "Még nincs rendelés" }),
@@ -2421,7 +2538,7 @@ const LegalPage = () => {
2421
2538
  ] }) })
2422
2539
  ] }) });
2423
2540
  };
2424
- function Field$3({
2541
+ function Field$4({
2425
2542
  label,
2426
2543
  children
2427
2544
  }) {
@@ -2467,398 +2584,281 @@ const config$7 = adminSdk.defineRouteConfig({
2467
2584
  icon: Newspaper,
2468
2585
  rank: 40
2469
2586
  });
2587
+ const defaults$1 = {
2588
+ ARUKERESO_STOREFRONT_URL: "",
2589
+ ARUKERESO_PRODUCT_PATH_TEMPLATE: "/{country_code}/products/{handle}?v_id={variant_id}",
2590
+ ARUKERESO_SALES_CHANNEL_ID: "",
2591
+ ARUKERESO_DEFAULT_MANUFACTURER: "",
2592
+ ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER: false,
2593
+ ARUKERESO_INCLUDE_OUT_OF_STOCK: true,
2594
+ ARUKERESO_DEFAULT_DELIVERY_TIME: "",
2595
+ ARUKERESO_DEFAULT_DELIVERY_COST: ""
2596
+ };
2470
2597
  const backendUrl$8 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
2471
- const adminBase$4 = (__BASE__ || "/app").replace(/\/+$/, "");
2472
- function apiUrl$8(path) {
2473
- return `${backendUrl$8}${path.startsWith("/") ? path : `/${path}`}`;
2474
- }
2475
- const BillingPage = () => {
2476
- const [orders, setOrders] = React.useState([]);
2477
- const [configuration, setConfiguration] = React.useState(null);
2478
- const [selected, setSelected] = React.useState([]);
2479
- const [query, setQuery] = React.useState("");
2480
- const [loading, setLoading] = React.useState(null);
2481
- const [listLoading, setListLoading] = React.useState(true);
2482
- const [loadError, setLoadError] = React.useState(null);
2598
+ const apiUrl$8 = (path) => `${backendUrl$8}${path.startsWith("/") ? path : `/${path}`}`;
2599
+ const ArukeresoPage = () => {
2600
+ const [enabled, setEnabled] = React.useState(false);
2601
+ const [draft, setDraft] = React.useState(defaults$1);
2602
+ const [loading, setLoading] = React.useState(true);
2603
+ const [saving, setSaving] = React.useState(false);
2604
+ const [previewing, setPreviewing] = React.useState(false);
2605
+ const [preview, setPreview] = React.useState(null);
2606
+ const update = (key, value) => {
2607
+ setDraft((current) => ({ ...current, [key]: value }));
2608
+ };
2483
2609
  const load = React.useCallback(async () => {
2484
- setListLoading(true);
2485
- setLoadError(null);
2610
+ setLoading(true);
2486
2611
  try {
2487
- const response = await fetch(apiUrl$8("/admin/billingo/orders"), {
2612
+ const response = await fetch(apiUrl$8("/admin/integrations/arukereso"), {
2488
2613
  credentials: "include"
2489
2614
  });
2490
- const payload = await response.json().catch(() => ({}));
2491
- if (!response.ok) {
2492
- throw new Error(payload.message || "A számlázási lista nem tölthető be.");
2615
+ const data = await response.json().catch(() => ({}));
2616
+ if (!response.ok || !data.integration) {
2617
+ throw new Error(data.message || "Az Árukereső beállítások nem tölthetők be.");
2493
2618
  }
2494
- const nextOrders = payload.orders || [];
2495
- setOrders(nextOrders);
2496
- setConfiguration(payload.configuration || null);
2497
- setSelected(
2498
- (current) => current.filter((id) => nextOrders.some((order) => order.id === id))
2499
- );
2619
+ const values = data.integration.values || {};
2620
+ setEnabled(Boolean(data.integration.enabled));
2621
+ setDraft({
2622
+ ARUKERESO_STOREFRONT_URL: values.ARUKERESO_STOREFRONT_URL || "",
2623
+ ARUKERESO_PRODUCT_PATH_TEMPLATE: values.ARUKERESO_PRODUCT_PATH_TEMPLATE || "/{country_code}/products/{handle}?v_id={variant_id}",
2624
+ ARUKERESO_SALES_CHANNEL_ID: values.ARUKERESO_SALES_CHANNEL_ID || "",
2625
+ ARUKERESO_DEFAULT_MANUFACTURER: values.ARUKERESO_DEFAULT_MANUFACTURER || "",
2626
+ ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER: values.ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER === "true",
2627
+ ARUKERESO_INCLUDE_OUT_OF_STOCK: values.ARUKERESO_INCLUDE_OUT_OF_STOCK !== "false",
2628
+ ARUKERESO_DEFAULT_DELIVERY_TIME: values.ARUKERESO_DEFAULT_DELIVERY_TIME || "",
2629
+ ARUKERESO_DEFAULT_DELIVERY_COST: values.ARUKERESO_DEFAULT_DELIVERY_COST || ""
2630
+ });
2500
2631
  } catch (error) {
2501
- setOrders([]);
2502
- setLoadError(
2503
- error instanceof Error ? error.message : "A számlázási lista nem tölthető be."
2504
- );
2632
+ ui.toast.error(error instanceof Error ? error.message : "Betöltési hiba.");
2505
2633
  } finally {
2506
- setListLoading(false);
2634
+ setLoading(false);
2507
2635
  }
2508
2636
  }, []);
2509
2637
  React.useEffect(() => {
2510
2638
  void load();
2511
2639
  }, [load]);
2512
- const visible = React.useMemo(() => {
2513
- const value = query.trim().toLowerCase();
2514
- if (!value) return orders;
2515
- return orders.filter(
2516
- (order) => [
2517
- order.display_id,
2518
- order.email,
2519
- order.invoice?.invoiceNumber,
2520
- order.invoice?.modificationInvoiceNumber
2521
- ].some(
2522
- (item) => String(item || "").toLowerCase().includes(value)
2523
- )
2524
- );
2525
- }, [orders, query]);
2526
- const selectedSet = React.useMemo(() => new Set(selected), [selected]);
2527
- const toggle = (id) => {
2528
- setSelected(
2529
- (current) => current.includes(id) ? current.filter((item) => item !== id) : [...current, id]
2530
- );
2531
- };
2532
- const bulkCreate = async () => {
2533
- if (!selected.length) {
2534
- ui.toast.error("Jelölj ki legalább egy rendelést.");
2535
- return;
2536
- }
2537
- setLoading("bulk");
2538
- try {
2539
- const response = await fetch(apiUrl$8("/admin/billingo/bulk/create"), {
2540
- method: "POST",
2541
- credentials: "include",
2542
- headers: { "Content-Type": "application/json" },
2543
- body: JSON.stringify({ orderIds: selected })
2544
- });
2545
- const payload = await response.json().catch(() => ({}));
2546
- if (!response.ok && response.status !== 207) {
2547
- throw new Error(payload.message || "A tömeges számlázás nem sikerült.");
2548
- }
2549
- const failed = payload.results?.filter((item) => !item.ok).length || 0;
2550
- const created = payload.results?.filter(
2551
- (item) => item.ok && !item.alreadyExisted
2552
- ).length || 0;
2553
- if (failed) {
2554
- ui.toast.error(`${failed} számla nem készült el; ${created} sikeres.`);
2555
- } else {
2556
- ui.toast.success(`${created} új számla elkészült.`);
2557
- }
2558
- await load();
2559
- } catch (error) {
2560
- ui.toast.error(
2561
- error instanceof Error ? error.message : "A tömeges számlázás nem sikerült."
2562
- );
2563
- } finally {
2564
- setLoading(null);
2565
- }
2566
- };
2567
- const action = async (order, value) => {
2568
- if (value === "cancel" && !confirm(
2569
- `Biztosan sztornózod a(z) ${order.invoice?.invoiceNumber || order.invoice?.documentId} számlát?`
2570
- )) {
2571
- return;
2572
- }
2573
- if (value === "modify" && !confirm(
2574
- `Biztosan teljes jóváíró/módosító bizonylatot készítesz a(z) ${order.invoice?.invoiceNumber || order.invoice?.documentId} számlához? Ez a teljes számla összegét jóváírja.`
2575
- )) {
2576
- return;
2577
- }
2578
- setLoading(`${order.id}:${value}`);
2640
+ const save = async () => {
2641
+ setSaving(true);
2579
2642
  try {
2580
- const response = await fetch(apiUrl$8(`/admin/orders/${order.id}/billingo`), {
2643
+ const response = await fetch(apiUrl$8("/admin/integrations/arukereso"), {
2581
2644
  method: "POST",
2582
2645
  credentials: "include",
2583
2646
  headers: { "Content-Type": "application/json" },
2584
- body: JSON.stringify({ action: value })
2647
+ body: JSON.stringify({ enabled, values: draft })
2585
2648
  });
2586
- const payload = await response.json().catch(() => ({}));
2587
- if (!response.ok) {
2588
- throw new Error(payload.message || "A Billingo művelet nem sikerült.");
2589
- }
2590
- const messages = {
2591
- create: "A számla elkészült.",
2592
- sync: "A számla frissítve.",
2593
- send: "A számla elküldve.",
2594
- cancel: "A számla sztornózva.",
2595
- modify: "A teljes jóváíró/módosító bizonylat elkészült."
2596
- };
2597
- ui.toast.success(messages[value]);
2598
- if (value === "modify" && payload.sendError) {
2599
- ui.toast.error(`A jóváírás elkészült, de az e-mail küldése sikertelen: ${payload.sendError}`);
2600
- }
2649
+ const data = await response.json().catch(() => ({}));
2650
+ if (!response.ok) throw new Error(data.message || "A mentés nem sikerült.");
2651
+ ui.toast.success(data.message || "Árukereső beállítások mentve.");
2652
+ setPreview(null);
2601
2653
  await load();
2602
2654
  } catch (error) {
2603
- ui.toast.error(
2604
- error instanceof Error ? error.message : "A Billingo művelet nem sikerült."
2605
- );
2655
+ ui.toast.error(error instanceof Error ? error.message : "A mentés nem sikerült.");
2606
2656
  } finally {
2607
- setLoading(null);
2657
+ setSaving(false);
2608
2658
  }
2609
2659
  };
2610
- const testConnection = async () => {
2611
- setLoading("connection");
2660
+ const runPreview = async () => {
2661
+ setPreviewing(true);
2612
2662
  try {
2613
- const response = await fetch(apiUrl$8("/admin/billingo/connection"), {
2614
- method: "POST",
2663
+ const response = await fetch(apiUrl$8("/admin/arukereso/preview"), {
2615
2664
  credentials: "include"
2616
2665
  });
2617
- const payload = await response.json().catch(() => ({}));
2618
- if (!response.ok) {
2619
- throw new Error(payload.message || "A Billingo kapcsolat nem működik.");
2620
- }
2621
- ui.toast.success(payload.message || "A Billingo kapcsolat működik.");
2666
+ const data = await response.json().catch(() => ({}));
2667
+ if (!response.ok) throw new Error(data.message || "A feed nem generálható.");
2668
+ setPreview(data);
2669
+ ui.toast.success("Az Árukereső feed ellenőrzése elkészült.");
2622
2670
  } catch (error) {
2623
- ui.toast.error(
2624
- error instanceof Error ? error.message : "A Billingo kapcsolat nem működik."
2625
- );
2671
+ ui.toast.error(error instanceof Error ? error.message : "A feed nem generálható.");
2626
2672
  } finally {
2627
- setLoading(null);
2673
+ setPreviewing(false);
2628
2674
  }
2629
2675
  };
2676
+ const feedUrl = apiUrl$8("/feeds/arukereso.xml");
2677
+ const d = preview?.diagnostics;
2630
2678
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
2631
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 border-b px-6 py-4", children: [
2679
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4 border-b px-6 py-4", children: [
2632
2680
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2633
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Számlázás" }),
2634
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Billingo számlák, sztornók és teljes jóváírások kezelése a HSZRDM Commerce rendeléseiből." }),
2635
- configuration?.autoInvoice ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
2636
- "Az automatikus számlázás aktív",
2637
- configuration.autoSend ? ", automatikus e-mail küldéssel" : "",
2638
- "."
2639
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-warning", children: "Az automatikus számlázás ki van kapcsolva." })
2681
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Árukereső.hu" }),
2682
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Árukereső XML feed a Medusa termék-, variant-, HU/HUF ár-, ÁFA- és készletadataiból." })
2640
2683
  ] }),
2641
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2642
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { children: [
2643
- selected.length,
2644
- " kijelölve"
2645
- ] }),
2684
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
2685
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: enabled, onCheckedChange: setEnabled, disabled: loading }),
2686
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: enabled ? "Bekapcsolva" : "Kikapcsolva" })
2687
+ ] })
2688
+ ] }),
2689
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 border-b px-6 py-5 md:grid-cols-2", children: [
2690
+ /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "Storefront URL", children: /* @__PURE__ */ jsxRuntime.jsx(
2691
+ ui.Input,
2692
+ {
2693
+ value: draft.ARUKERESO_STOREFRONT_URL,
2694
+ onChange: (e) => update("ARUKERESO_STOREFRONT_URL", e.target.value),
2695
+ placeholder: "https://shop.hu (üresen STOREFRONT_URL)"
2696
+ }
2697
+ ) }),
2698
+ /* @__PURE__ */ jsxRuntime.jsxs(Field$3, { label: "Termék URL sablon", children: [
2646
2699
  /* @__PURE__ */ jsxRuntime.jsx(
2647
- ui.Button,
2700
+ ui.Input,
2648
2701
  {
2649
- variant: "secondary",
2650
- isLoading: loading === "connection",
2651
- onClick: () => void testConnection(),
2652
- children: "Kapcsolat tesztelése"
2702
+ value: draft.ARUKERESO_PRODUCT_PATH_TEMPLATE,
2703
+ onChange: (e) => update("ARUKERESO_PRODUCT_PATH_TEMPLATE", e.target.value)
2653
2704
  }
2654
- )
2655
- ] })
2656
- ] }),
2657
- configuration && !configuration.configured ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-ui-border-error bg-ui-bg-base px-6 py-4", children: [
2658
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-error", children: "A Billingo még nincs teljesen beállítva." }),
2659
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-subtle", children: [
2660
- "Hiányzik: ",
2661
- [
2662
- !configuration.apiKey && "API kulcs",
2663
- !configuration.documentBlockId && "számlatömb azonosító"
2664
- ].filter(Boolean).join(", "),
2665
- ". Állítsd be a Settings → Integrációk → Billingo oldalon, majd indítsd újra a backendet."
2666
- ] })
2667
- ] }) : null,
2668
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3 border-b px-6 py-4", children: [
2669
- /* @__PURE__ */ jsxRuntime.jsx(
2705
+ ),
2706
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-xs", children: [
2707
+ "Helyőrzők: ",
2708
+ "{country_code}",
2709
+ ", ",
2710
+ "{currency_code}",
2711
+ ", ",
2712
+ "{handle}",
2713
+ ", ",
2714
+ "{variant_id}",
2715
+ "."
2716
+ ] })
2717
+ ] }),
2718
+ /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "Sales Channel ID", children: /* @__PURE__ */ jsxRuntime.jsx(
2670
2719
  ui.Input,
2671
2720
  {
2672
- className: "max-w-sm",
2673
- value: query,
2674
- onChange: (event) => setQuery(event.target.value),
2675
- placeholder: "Rendelés, e-mail vagy számlaszám keresése"
2721
+ value: draft.ARUKERESO_SALES_CHANNEL_ID,
2722
+ onChange: (e) => update("ARUKERESO_SALES_CHANNEL_ID", e.target.value),
2723
+ placeholder: "sc_... (opcionális)"
2676
2724
  }
2677
- ),
2678
- /* @__PURE__ */ jsxRuntime.jsx(
2679
- ui.Button,
2725
+ ) }),
2726
+ /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "Alapértelmezett gyártó", children: /* @__PURE__ */ jsxRuntime.jsx(
2727
+ ui.Input,
2680
2728
  {
2681
- disabled: Boolean(loading) || !configuration?.configured,
2682
- isLoading: loading === "bulk",
2683
- onClick: () => void bulkCreate(),
2684
- children: "Kijelöltek számlázása"
2729
+ value: draft.ARUKERESO_DEFAULT_MANUFACTURER,
2730
+ onChange: (e) => update("ARUKERESO_DEFAULT_MANUFACTURER", e.target.value),
2731
+ placeholder: "Csak ha minden termékre tényleg igaz"
2685
2732
  }
2686
- ),
2687
- /* @__PURE__ */ jsxRuntime.jsx(
2688
- ui.Button,
2733
+ ) }),
2734
+ /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "SKU mint gyártói cikkszám", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center gap-2", children: [
2735
+ /* @__PURE__ */ jsxRuntime.jsx(
2736
+ ui.Switch,
2737
+ {
2738
+ checked: draft.ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER,
2739
+ onCheckedChange: (value) => update("ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER", value)
2740
+ }
2741
+ ),
2742
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: draft.ARUKERESO_USE_SKU_AS_PRODUCT_NUMBER ? "Igen" : "Nem" })
2743
+ ] }) }),
2744
+ /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "Készlethiányos termékek", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center gap-2", children: [
2745
+ /* @__PURE__ */ jsxRuntime.jsx(
2746
+ ui.Switch,
2747
+ {
2748
+ checked: draft.ARUKERESO_INCLUDE_OUT_OF_STOCK,
2749
+ onCheckedChange: (value) => update("ARUKERESO_INCLUDE_OUT_OF_STOCK", value)
2750
+ }
2751
+ ),
2752
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: draft.ARUKERESO_INCLUDE_OUT_OF_STOCK ? "DeliveryTime=NO" : "Kimaradnak" })
2753
+ ] }) }),
2754
+ /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "Alap szállítási idő", children: /* @__PURE__ */ jsxRuntime.jsx(
2755
+ ui.Input,
2689
2756
  {
2690
- variant: "secondary",
2691
- disabled: Boolean(loading),
2692
- onClick: () => void load(),
2693
- children: "Frissítés"
2757
+ value: draft.ARUKERESO_DEFAULT_DELIVERY_TIME,
2758
+ onChange: (e) => update("ARUKERESO_DEFAULT_DELIVERY_TIME", e.target.value),
2759
+ placeholder: "1 vagy 3"
2694
2760
  }
2695
- )
2761
+ ) }),
2762
+ /* @__PURE__ */ jsxRuntime.jsx(Field$3, { label: "Alap szállítási költség", children: /* @__PURE__ */ jsxRuntime.jsx(
2763
+ ui.Input,
2764
+ {
2765
+ value: draft.ARUKERESO_DEFAULT_DELIVERY_COST,
2766
+ onChange: (e) => update("ARUKERESO_DEFAULT_DELIVERY_COST", e.target.value),
2767
+ placeholder: "1990 vagy FREE"
2768
+ }
2769
+ ) })
2696
2770
  ] }),
2697
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-x-auto", children: [
2698
- /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-left text-sm", children: [
2699
- /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "border-b bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
2700
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
2701
- ui.Checkbox,
2702
- {
2703
- checked: visible.length > 0 && visible.every((order) => selectedSet.has(order.id)),
2704
- onCheckedChange: () => setSelected(
2705
- visible.every((order) => selectedSet.has(order.id)) ? selected.filter(
2706
- (id) => !visible.some((order) => order.id === id)
2707
- ) : [
2708
- .../* @__PURE__ */ new Set([
2709
- ...selected,
2710
- ...visible.map((order) => order.id)
2711
- ])
2712
- ]
2713
- )
2714
- }
2715
- ) }),
2716
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Rendelés" }),
2717
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Fizetés" }),
2718
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Számla" }),
2719
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Billingo státusz" }),
2720
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Műveletek" })
2721
- ] }) }),
2722
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: visible.map((order) => {
2723
- const invoice = order.invoice;
2724
- const last = order.operations?.at(-1);
2725
- const modified = Boolean(invoice?.modificationDocumentId);
2726
- return /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b align-top", children: [
2727
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
2728
- ui.Checkbox,
2729
- {
2730
- checked: selectedSet.has(order.id),
2731
- onCheckedChange: () => toggle(order.id)
2732
- }
2733
- ) }),
2734
- /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2735
- /* @__PURE__ */ jsxRuntime.jsxs(
2736
- "a",
2737
- {
2738
- className: "text-ui-fg-interactive",
2739
- href: `${adminBase$4}/orders/${order.id}`,
2740
- children: [
2741
- "#",
2742
- order.display_id ?? order.id
2743
- ]
2744
- }
2745
- ),
2746
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: order.email || "—" }),
2747
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: formatMoney(order.total, order.currency_code) })
2748
- ] }),
2749
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: order.payment_status === "captured" ? "Fizetve" : order.payment_status === "refunded" ? "Visszatérítve" : order.payment_status === "partially_refunded" ? "Részben visszatérítve" : order.payment_status || "—" }),
2750
- /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2751
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: invoice?.invoiceNumber || (invoice ? `#${invoice.documentId}` : "Még nincs") }),
2752
- invoice?.cancelled ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-error", children: "Sztornózva" }) : null,
2753
- modified ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-interactive", children: [
2754
- "Jóváírva: ",
2755
- invoice?.modificationInvoiceNumber || `#${invoice?.modificationDocumentId}`
2756
- ] }) : null,
2757
- last?.status === "failed" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xs text-ui-fg-error", children: last.message }) : null
2758
- ] }),
2759
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: invoice ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2760
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: invoice.paymentStatus || "—" }),
2761
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle", children: [
2762
- "NAV: ",
2763
- invoice.onlineSzamlaStatus || "—"
2764
- ] }),
2765
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle", children: [
2766
- "E-mail: ",
2767
- invoice.notificationStatus || "—"
2768
- ] })
2769
- ] }) : "—" }),
2770
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-w-lg flex-wrap gap-2", children: !invoice ? /* @__PURE__ */ jsxRuntime.jsx(
2771
- ui.Button,
2772
- {
2773
- size: "small",
2774
- disabled: !configuration?.configured || Boolean(loading),
2775
- isLoading: loading === `${order.id}:create`,
2776
- onClick: () => void action(order, "create"),
2777
- children: "Számla készítése"
2778
- }
2779
- ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2780
- /* @__PURE__ */ jsxRuntime.jsx(
2781
- ui.Button,
2782
- {
2783
- size: "small",
2784
- variant: "secondary",
2785
- disabled: Boolean(loading),
2786
- isLoading: loading === `${order.id}:sync`,
2787
- onClick: () => void action(order, "sync"),
2788
- children: "Frissítés"
2789
- }
2790
- ),
2791
- /* @__PURE__ */ jsxRuntime.jsx(
2792
- "a",
2793
- {
2794
- href: apiUrl$8(`/admin/orders/${order.id}/billingo/pdf`),
2795
- target: "_blank",
2796
- rel: "noreferrer",
2797
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: "PDF" })
2798
- }
2799
- ),
2800
- /* @__PURE__ */ jsxRuntime.jsx(
2801
- ui.Button,
2802
- {
2803
- size: "small",
2804
- variant: "secondary",
2805
- disabled: Boolean(loading) || invoice.cancelled,
2806
- isLoading: loading === `${order.id}:send`,
2807
- onClick: () => void action(order, "send"),
2808
- children: "E-mail"
2809
- }
2810
- ),
2811
- /* @__PURE__ */ jsxRuntime.jsx(
2812
- ui.Button,
2813
- {
2814
- size: "small",
2815
- variant: "secondary",
2816
- disabled: Boolean(loading) || invoice.cancelled || modified,
2817
- isLoading: loading === `${order.id}:modify`,
2818
- onClick: () => void action(order, "modify"),
2819
- children: "Teljes jóváírás"
2820
- }
2821
- ),
2822
- /* @__PURE__ */ jsxRuntime.jsx(
2823
- ui.Button,
2824
- {
2825
- size: "small",
2826
- variant: "danger",
2827
- disabled: Boolean(loading) || invoice.cancelled || modified,
2828
- isLoading: loading === `${order.id}:cancel`,
2829
- onClick: () => void action(order, "cancel"),
2830
- children: "Sztornó"
2831
- }
2832
- )
2833
- ] }) }) })
2834
- ] }, order.id);
2835
- }) })
2771
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b px-6 py-4", children: [
2772
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2773
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: "Publikus feed URL" }),
2774
+ /* @__PURE__ */ jsxRuntime.jsx("a", { className: "break-all text-ui-fg-interactive hover:underline", href: feedUrl, target: "_blank", rel: "noreferrer", children: feedUrl })
2775
+ ] }),
2776
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2777
+ /* @__PURE__ */ jsxRuntime.jsx(
2778
+ ui.Button,
2779
+ {
2780
+ variant: "secondary",
2781
+ isLoading: previewing,
2782
+ disabled: !enabled || loading || saving || previewing,
2783
+ onClick: () => void runPreview(),
2784
+ children: "Feed ellenőrzése"
2785
+ }
2786
+ ),
2787
+ /* @__PURE__ */ jsxRuntime.jsx(
2788
+ ui.Button,
2789
+ {
2790
+ isLoading: saving,
2791
+ disabled: loading || saving || previewing,
2792
+ onClick: () => void save(),
2793
+ children: "Mentés"
2794
+ }
2795
+ )
2796
+ ] })
2797
+ ] }),
2798
+ d ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2799
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 border-b px-6 py-5 sm:grid-cols-2 lg:grid-cols-4", children: [
2800
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Termékek", value: d.productsScanned }),
2801
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Variantok", value: d.variantsScanned }),
2802
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Feed tételek", value: d.itemsEmitted }),
2803
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs gyártó", value: d.skippedNoManufacturer }),
2804
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs kategória", value: d.skippedNoCategory }),
2805
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs ár", value: d.skippedNoPrice }),
2806
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs ProductNumber", value: d.itemsMissingProductNumber }),
2807
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Nincs kép", value: d.itemsMissingImage }),
2808
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Készlethiány miatt kimaradt", value: d.skippedOutOfStock }),
2809
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Sales channel hiba", value: d.skippedNoSalesChannel + d.skippedNotInSalesChannel }),
2810
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Hibás delivery time", value: d.invalidDeliveryTime }),
2811
+ /* @__PURE__ */ jsxRuntime.jsx(Stat$1, { label: "Hibás delivery cost", value: d.invalidDeliveryCost })
2836
2812
  ] }),
2837
- listLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Számlázási rendelések betöltése…" }) }) : null,
2838
- !listLoading && loadError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "m-6 flex items-center justify-between gap-4 rounded-lg border border-ui-border-error p-4", children: [
2839
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2840
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-error", children: "Betöltési hiba." }),
2841
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: loadError })
2842
- ] }),
2843
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: () => void load(), children: "Újrapróbálás" })
2844
- ] }) : null,
2845
- !listLoading && !loadError && !visible.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Nincs megjeleníthető rendelés." }) }) : null
2846
- ] })
2813
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-left text-sm", children: [
2814
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "border-b bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
2815
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Identifier" }),
2816
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Termék" }),
2817
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Gyártó" }),
2818
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Kategória" }),
2819
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Bruttó / nettó" }),
2820
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Szállítás" })
2821
+ ] }) }),
2822
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: (preview?.sample || []).map((item) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b", children: [
2823
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: item.identifier }) }),
2824
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2825
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.productUrl, target: "_blank", rel: "noreferrer", className: "text-ui-fg-interactive hover:underline", children: item.name }),
2826
+ item.productNumber ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-muted text-xs", children: [
2827
+ "PN: ",
2828
+ item.productNumber
2829
+ ] }) : null
2830
+ ] }),
2831
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.manufacturer }),
2832
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.category }),
2833
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2834
+ item.price,
2835
+ " / ",
2836
+ item.netPrice,
2837
+ " HUF"
2838
+ ] }),
2839
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
2840
+ item.deliveryTime || "—",
2841
+ " · ",
2842
+ item.deliveryCost || "—"
2843
+ ] })
2844
+ ] }, item.identifier)) })
2845
+ ] }) })
2846
+ ] }) : null
2847
2847
  ] });
2848
2848
  };
2849
- function formatMoney(value, currency) {
2850
- if (typeof value !== "number") return "—";
2851
- return new Intl.NumberFormat("hu-HU", {
2852
- style: "currency",
2853
- currency: (currency || "HUF").toUpperCase(),
2854
- maximumFractionDigits: currency?.toUpperCase() === "HUF" ? 0 : 2
2855
- }).format(value);
2849
+ function Field$3({ label, children }) {
2850
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
2851
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: label }),
2852
+ children
2853
+ ] });
2856
2854
  }
2857
- const config$6 = adminSdk.defineRouteConfig({
2858
- label: "Számlázás",
2859
- icon: Newspaper,
2860
- rank: 36
2861
- });
2855
+ function Stat$1({ label, value }) {
2856
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-3", children: [
2857
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: label }),
2858
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: value })
2859
+ ] });
2860
+ }
2861
+ const config$6 = adminSdk.defineRouteConfig({ label: "Árukereső", rank: 42 });
2862
2862
  const EMPTY_SEO = {
2863
2863
  title: "",
2864
2864
  description: "",
@@ -3124,251 +3124,60 @@ ${selectedPage.path}`)) return;
3124
3124
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-ui-fg-subtle", children: previewDescription })
3125
3125
  ] }),
3126
3126
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2 border-t pt-5", children: [
3127
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { isLoading: saving, disabled: saving || deleting || mode === "page" && !pagePath.trim(), onClick: () => void save(), children: "Mentés" }),
3128
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", disabled: saving || deleting, onClick: () => setForm({ ...EMPTY_SEO }), children: "SEO felülírások ürítése" }),
3129
- mode === "page" && selectedPage ? /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { className: "ml-auto", variant: "secondary", isLoading: deleting, disabled: saving || deleting, onClick: () => void removePage(), children: "Oldal törlése" }) : null
3130
- ] })
3131
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Válassz egy elemet a SEO szerkesztéséhez." }) })
3132
- ] })
3133
- ] });
3134
- };
3135
- const ModeButton = ({ active, onClick, children }) => /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: active ? "primary" : "secondary", onClick, children });
3136
- const ListButton = ({ active, title, subtitle, onClick }) => /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick, className: `mb-2 w-full rounded-lg border p-3 text-left transition-colors hover:bg-ui-bg-subtle ${active ? "bg-ui-bg-subtle" : "bg-ui-bg-base"}`, children: [
3137
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate font-medium", children: title }),
3138
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 truncate text-xs text-ui-fg-subtle", children: subtitle })
3139
- ] });
3140
- const Field$2 = ({ label, help, children }) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
3141
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: label }),
3142
- children,
3143
- help ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-muted", children: help }) : null
3144
- ] });
3145
- const Counter = ({ value, recommended }) => {
3146
- const length = value.length;
3147
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: `text-xs ${length > recommended ? "text-ui-fg-warning" : "text-ui-fg-muted"}`, children: [
3148
- length,
3149
- " karakter",
3150
- length > recommended ? ` · ajánlott maximum kb. ${recommended}` : ""
3151
- ] });
3152
- };
3153
- const ToggleCard = ({ label, description, checked, onCheckedChange }) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-start justify-between gap-4 rounded-lg border p-4", children: [
3154
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3155
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: label }),
3156
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-subtle", children: description })
3157
- ] }),
3158
- /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked, onCheckedChange })
3159
- ] });
3160
- function normalizeSeo(value) {
3161
- return {
3162
- title: value?.title || "",
3163
- description: value?.description || "",
3164
- canonical: value?.canonical || "",
3165
- noindex: Boolean(value?.noindex),
3166
- nofollow: Boolean(value?.nofollow),
3167
- ogTitle: value?.ogTitle || "",
3168
- ogDescription: value?.ogDescription || "",
3169
- ogImage: value?.ogImage || ""
3170
- };
3171
- }
3172
- function normalizeEntity(value) {
3173
- return { ...value, seo: normalizeSeo(value.seo) };
3174
- }
3175
- function normalizePage(value) {
3176
- return { ...value, seo: normalizeSeo(value.seo) };
3177
- }
3178
- const defaults = {
3179
- GOOGLE_MERCHANT_COUNTRY_CODE: "HU",
3180
- GOOGLE_MERCHANT_CURRENCY_CODE: "HUF",
3181
- GOOGLE_MERCHANT_STOREFRONT_URL: "",
3182
- GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE: "/{country_code}/products/{handle}?v_id={variant_id}",
3183
- GOOGLE_MERCHANT_SALES_CHANNEL_ID: "",
3184
- GOOGLE_MERCHANT_DEFAULT_BRAND: "",
3185
- GOOGLE_MERCHANT_DEFAULT_CONDITION: "new",
3186
- GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK: true,
3187
- GOOGLE_MERCHANT_FEED_TITLE: "",
3188
- GOOGLE_MERCHANT_FEED_DESCRIPTION: ""
3189
- };
3190
- const backendUrl$6 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
3191
- const apiUrl$6 = (path) => `${backendUrl$6}${path.startsWith("/") ? path : `/${path}`}`;
3192
- const GoogleMerchantPage = () => {
3193
- const [enabled, setEnabled] = React.useState(false);
3194
- const [draft, setDraft] = React.useState(defaults);
3195
- const [loading, setLoading] = React.useState(true);
3196
- const [saving, setSaving] = React.useState(false);
3197
- const [previewing, setPreviewing] = React.useState(false);
3198
- const [preview, setPreview] = React.useState(null);
3199
- const update = (key, value) => {
3200
- setDraft((current) => ({ ...current, [key]: value }));
3201
- };
3202
- const load = React.useCallback(async () => {
3203
- setLoading(true);
3204
- try {
3205
- const response = await fetch(apiUrl$6("/admin/integrations/google-merchant"), {
3206
- credentials: "include"
3207
- });
3208
- const data = await response.json().catch(() => ({}));
3209
- if (!response.ok || !data.integration) {
3210
- throw new Error(data.message || "A Google Merchant beállítások nem tölthetők be.");
3211
- }
3212
- const values = data.integration.values || {};
3213
- setEnabled(Boolean(data.integration.enabled));
3214
- setDraft({
3215
- GOOGLE_MERCHANT_COUNTRY_CODE: values.GOOGLE_MERCHANT_COUNTRY_CODE || "HU",
3216
- GOOGLE_MERCHANT_CURRENCY_CODE: values.GOOGLE_MERCHANT_CURRENCY_CODE || "HUF",
3217
- GOOGLE_MERCHANT_STOREFRONT_URL: values.GOOGLE_MERCHANT_STOREFRONT_URL || "",
3218
- GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE: values.GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE || "/{country_code}/products/{handle}?v_id={variant_id}",
3219
- GOOGLE_MERCHANT_SALES_CHANNEL_ID: values.GOOGLE_MERCHANT_SALES_CHANNEL_ID || "",
3220
- GOOGLE_MERCHANT_DEFAULT_BRAND: values.GOOGLE_MERCHANT_DEFAULT_BRAND || "",
3221
- GOOGLE_MERCHANT_DEFAULT_CONDITION: values.GOOGLE_MERCHANT_DEFAULT_CONDITION || "new",
3222
- GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK: values.GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK !== "false",
3223
- GOOGLE_MERCHANT_FEED_TITLE: values.GOOGLE_MERCHANT_FEED_TITLE || "",
3224
- GOOGLE_MERCHANT_FEED_DESCRIPTION: values.GOOGLE_MERCHANT_FEED_DESCRIPTION || ""
3225
- });
3226
- } catch (error) {
3227
- ui.toast.error(error instanceof Error ? error.message : "Betöltési hiba.");
3228
- } finally {
3229
- setLoading(false);
3230
- }
3231
- }, []);
3232
- React.useEffect(() => {
3233
- void load();
3234
- }, [load]);
3235
- const save = async () => {
3236
- setSaving(true);
3237
- try {
3238
- const response = await fetch(apiUrl$6("/admin/integrations/google-merchant"), {
3239
- method: "POST",
3240
- credentials: "include",
3241
- headers: { "Content-Type": "application/json" },
3242
- body: JSON.stringify({ enabled, values: draft })
3243
- });
3244
- const data = await response.json().catch(() => ({}));
3245
- if (!response.ok) throw new Error(data.message || "A mentés nem sikerült.");
3246
- ui.toast.success(data.message || "Google Merchant beállítások mentve.");
3247
- setPreview(null);
3248
- await load();
3249
- } catch (error) {
3250
- ui.toast.error(error instanceof Error ? error.message : "A mentés nem sikerült.");
3251
- } finally {
3252
- setSaving(false);
3253
- }
3254
- };
3255
- const runPreview = async () => {
3256
- setPreviewing(true);
3257
- try {
3258
- const response = await fetch(apiUrl$6("/admin/google-merchant/preview"), {
3259
- credentials: "include"
3260
- });
3261
- const data = await response.json().catch(() => ({}));
3262
- if (!response.ok) throw new Error(data.message || "A feed nem generálható.");
3263
- setPreview(data);
3264
- ui.toast.success("A Merchant feed ellenőrzése elkészült.");
3265
- } catch (error) {
3266
- ui.toast.error(error instanceof Error ? error.message : "A feed nem generálható.");
3267
- } finally {
3268
- setPreviewing(false);
3269
- }
3270
- };
3271
- const feedUrl = apiUrl$6("/feeds/google-merchant.xml");
3272
- const d = preview?.diagnostics;
3273
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
3274
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4 border-b px-6 py-4", children: [
3275
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3276
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Google Merchant Center" }),
3277
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Google RSS 2.0 feed a Medusa termék-, variant-, régiós ár-, ÁFA- és készletadataiból." })
3278
- ] }),
3279
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
3280
- /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: enabled, onCheckedChange: setEnabled, disabled: loading }),
3281
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: enabled ? "Bekapcsolva" : "Kikapcsolva" })
3282
- ] })
3283
- ] }),
3284
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 border-b px-6 py-5 md:grid-cols-2", children: [
3285
- /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Célország", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_COUNTRY_CODE, onChange: (e) => update("GOOGLE_MERCHANT_COUNTRY_CODE", e.target.value.toUpperCase()), placeholder: "HU" }) }),
3286
- /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Pénznem", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_CURRENCY_CODE, onChange: (e) => update("GOOGLE_MERCHANT_CURRENCY_CODE", e.target.value.toUpperCase()), placeholder: "HUF" }) }),
3287
- /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Storefront URL", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_STOREFRONT_URL, onChange: (e) => update("GOOGLE_MERCHANT_STOREFRONT_URL", e.target.value), placeholder: "https://shop.hu (üresen STOREFRONT_URL)" }) }),
3288
- /* @__PURE__ */ jsxRuntime.jsxs(Field$1, { label: "Termék URL sablon", children: [
3289
- /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE, onChange: (e) => update("GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE", e.target.value) }),
3290
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-xs", children: [
3291
- "Helyőrzők: ",
3292
- "{country_code}",
3293
- ", ",
3294
- "{currency_code}",
3295
- ", ",
3296
- "{handle}",
3297
- ", ",
3298
- "{variant_id}",
3299
- "."
3127
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { isLoading: saving, disabled: saving || deleting || mode === "page" && !pagePath.trim(), onClick: () => void save(), children: "Mentés" }),
3128
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", disabled: saving || deleting, onClick: () => setForm({ ...EMPTY_SEO }), children: "SEO felülírások ürítése" }),
3129
+ mode === "page" && selectedPage ? /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { className: "ml-auto", variant: "secondary", isLoading: deleting, disabled: saving || deleting, onClick: () => void removePage(), children: "Oldal törlése" }) : null
3300
3130
  ] })
3301
- ] }),
3302
- /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Sales Channel ID", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_SALES_CHANNEL_ID, onChange: (e) => update("GOOGLE_MERCHANT_SALES_CHANNEL_ID", e.target.value), placeholder: "sc_... (opcionális)" }) }),
3303
- /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Alapértelmezett márka", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_DEFAULT_BRAND, onChange: (e) => update("GOOGLE_MERCHANT_DEFAULT_BRAND", e.target.value) }) }),
3304
- /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Alapértelmezett állapot", children: /* @__PURE__ */ jsxRuntime.jsxs("select", { className: "h-8 rounded-md border bg-ui-bg-field px-2 text-sm", value: draft.GOOGLE_MERCHANT_DEFAULT_CONDITION, onChange: (e) => update("GOOGLE_MERCHANT_DEFAULT_CONDITION", e.target.value), children: [
3305
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "new", children: "Új" }),
3306
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "refurbished", children: "Felújított" }),
3307
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "used", children: "Használt" })
3308
- ] }) }),
3309
- /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Készlethiányos variantok", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center gap-2", children: [
3310
- /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: draft.GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK, onCheckedChange: (value) => update("GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK", value) }),
3311
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: draft.GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK ? "Benne maradnak" : "Kimaradnak" })
3312
- ] }) }),
3313
- /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Feed neve", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_FEED_TITLE, onChange: (e) => update("GOOGLE_MERCHANT_FEED_TITLE", e.target.value), placeholder: "Üresen automatikus" }) }),
3314
- /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Feed leírása", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_FEED_DESCRIPTION, onChange: (e) => update("GOOGLE_MERCHANT_FEED_DESCRIPTION", e.target.value), placeholder: "Üresen automatikus" }) })
3315
- ] }),
3316
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b px-6 py-4", children: [
3317
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3318
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: "Publikus feed URL" }),
3319
- /* @__PURE__ */ jsxRuntime.jsx("a", { className: "break-all text-ui-fg-interactive hover:underline", href: feedUrl, target: "_blank", rel: "noreferrer", children: feedUrl })
3320
- ] }),
3321
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
3322
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", isLoading: previewing, disabled: !enabled || loading || saving || previewing, onClick: () => void runPreview(), children: "Feed ellenőrzése" }),
3323
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { isLoading: saving, disabled: loading || saving || previewing, onClick: () => void save(), children: "Mentés" })
3324
- ] })
3325
- ] }),
3326
- d ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3327
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 border-b px-6 py-5 sm:grid-cols-2 lg:grid-cols-4", children: [
3328
- /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Termékek", value: d.productsScanned }),
3329
- /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Variantok", value: d.variantsScanned }),
3330
- /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Feed tételek", value: d.itemsEmitted }),
3331
- /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Hiányzó identifier", value: d.itemsMissingIdentifiers }),
3332
- /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Nincs ár", value: d.skippedNoPrice }),
3333
- /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Nincs kép", value: d.skippedNoImage }),
3334
- /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Nincs sales channel", value: d.skippedNoSalesChannel + d.skippedNotInSalesChannel }),
3335
- /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Érvénytelen GTIN", value: d.invalidGtinsIgnored })
3336
- ] }),
3337
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-left text-sm", children: [
3338
- /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "border-b bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
3339
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "ID" }),
3340
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Termék" }),
3341
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Ár" }),
3342
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Készlet" }),
3343
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Identifier" })
3344
- ] }) }),
3345
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: (preview?.sample || []).map((item) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b", children: [
3346
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: item.id }) }),
3347
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.link, target: "_blank", rel: "noreferrer", className: "text-ui-fg-interactive hover:underline", children: item.title }) }),
3348
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.salePrice ? `${item.salePrice} (${item.price})` : item.price }),
3349
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.availability }),
3350
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.gtin || item.mpn || item.brand || "—" })
3351
- ] }, item.id)) })
3352
- ] }) })
3353
- ] }) : null
3131
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Válassz egy elemet a SEO szerkesztéséhez." }) })
3132
+ ] })
3354
3133
  ] });
3355
3134
  };
3356
- function Field$1({ label, children }) {
3357
- return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
3358
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: label }),
3359
- children
3135
+ const ModeButton = ({ active, onClick, children }) => /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: active ? "primary" : "secondary", onClick, children });
3136
+ const ListButton = ({ active, title, subtitle, onClick }) => /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick, className: `mb-2 w-full rounded-lg border p-3 text-left transition-colors hover:bg-ui-bg-subtle ${active ? "bg-ui-bg-subtle" : "bg-ui-bg-base"}`, children: [
3137
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate font-medium", children: title }),
3138
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 truncate text-xs text-ui-fg-subtle", children: subtitle })
3139
+ ] });
3140
+ const Field$2 = ({ label, help, children }) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
3141
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: label }),
3142
+ children,
3143
+ help ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-muted", children: help }) : null
3144
+ ] });
3145
+ const Counter = ({ value, recommended }) => {
3146
+ const length = value.length;
3147
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: `text-xs ${length > recommended ? "text-ui-fg-warning" : "text-ui-fg-muted"}`, children: [
3148
+ length,
3149
+ " karakter",
3150
+ length > recommended ? ` · ajánlott maximum kb. ${recommended}` : ""
3360
3151
  ] });
3152
+ };
3153
+ const ToggleCard = ({ label, description, checked, onCheckedChange }) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-start justify-between gap-4 rounded-lg border p-4", children: [
3154
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3155
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: label }),
3156
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-subtle", children: description })
3157
+ ] }),
3158
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked, onCheckedChange })
3159
+ ] });
3160
+ function normalizeSeo(value) {
3161
+ return {
3162
+ title: value?.title || "",
3163
+ description: value?.description || "",
3164
+ canonical: value?.canonical || "",
3165
+ noindex: Boolean(value?.noindex),
3166
+ nofollow: Boolean(value?.nofollow),
3167
+ ogTitle: value?.ogTitle || "",
3168
+ ogDescription: value?.ogDescription || "",
3169
+ ogImage: value?.ogImage || ""
3170
+ };
3361
3171
  }
3362
- function Stat({ label, value }) {
3363
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-3", children: [
3364
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: label }),
3365
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: value })
3366
- ] });
3172
+ function normalizeEntity(value) {
3173
+ return { ...value, seo: normalizeSeo(value.seo) };
3367
3174
  }
3368
- const config$5 = adminSdk.defineRouteConfig({ label: "Google Merchant", rank: 41 });
3369
- const backendUrl$5 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
3370
- function apiUrl$5(path) {
3371
- return `${backendUrl$5}${path.startsWith("/") ? path : `/${path}`}`;
3175
+ function normalizePage(value) {
3176
+ return { ...value, seo: normalizeSeo(value.seo) };
3177
+ }
3178
+ const backendUrl$6 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
3179
+ function apiUrl$6(path) {
3180
+ return `${backendUrl$6}${path.startsWith("/") ? path : `/${path}`}`;
3372
3181
  }
3373
3182
  const CoreAuditPage = () => {
3374
3183
  const [logs, setLogs] = React.useState([]);
@@ -3386,7 +3195,7 @@ const CoreAuditPage = () => {
3386
3195
  if (integration !== "all") params.set("integration", integration);
3387
3196
  if (status !== "all") params.set("status", status);
3388
3197
  const response = await fetch(
3389
- apiUrl$5(`/admin/core-audit?${params.toString()}`),
3198
+ apiUrl$6(`/admin/core-audit?${params.toString()}`),
3390
3199
  { credentials: "include" }
3391
3200
  );
3392
3201
  const payload = await response.json().catch(() => ({}));
@@ -3522,15 +3331,15 @@ function formatDate$3(value) {
3522
3331
  timeStyle: "short"
3523
3332
  }).format(date);
3524
3333
  }
3525
- const config$4 = adminSdk.defineRouteConfig({
3334
+ const config$5 = adminSdk.defineRouteConfig({
3526
3335
  label: "Audit napló",
3527
3336
  icon: Newspaper,
3528
3337
  rank: 39
3529
3338
  });
3530
- const backendUrl$4 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
3339
+ const backendUrl$5 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
3531
3340
  const adminBase$3 = (__BASE__ || "/app").replace(/\/+$/, "");
3532
- function apiUrl$4(path) {
3533
- return `${backendUrl$4}${path.startsWith("/") ? path : `/${path}`}`;
3341
+ function apiUrl$5(path) {
3342
+ return `${backendUrl$5}${path.startsWith("/") ? path : `/${path}`}`;
3534
3343
  }
3535
3344
  const IntegrationHealthPage = () => {
3536
3345
  const [integrations, setIntegrations] = React.useState([]);
@@ -3543,8 +3352,8 @@ const IntegrationHealthPage = () => {
3543
3352
  setLoadError(null);
3544
3353
  try {
3545
3354
  const [integrationsResponse, operationsResponse] = await Promise.all([
3546
- fetch(apiUrl$4("/admin/integrations"), { credentials: "include" }),
3547
- fetch(apiUrl$4("/admin/integration-operations?status=problems&limit=100"), {
3355
+ fetch(apiUrl$5("/admin/integrations"), { credentials: "include" }),
3356
+ fetch(apiUrl$5("/admin/integration-operations?status=problems&limit=100"), {
3548
3357
  credentials: "include"
3549
3358
  })
3550
3359
  ]);
@@ -3608,7 +3417,7 @@ const IntegrationHealthPage = () => {
3608
3417
  setTesting(integration.key);
3609
3418
  try {
3610
3419
  const response = await fetch(
3611
- apiUrl$4(`/admin/integrations/${integration.key}/test`),
3420
+ apiUrl$5(`/admin/integrations/${integration.key}/test`),
3612
3421
  {
3613
3422
  method: "POST",
3614
3423
  credentials: "include"
@@ -3756,206 +3565,66 @@ function SummaryCard$1({
3756
3565
  function formatDate$2(value) {
3757
3566
  const date = new Date(value);
3758
3567
  if (Number.isNaN(date.getTime())) return "—";
3759
- return new Intl.DateTimeFormat("hu-HU", {
3760
- dateStyle: "short",
3761
- timeStyle: "short"
3762
- }).format(date);
3763
- }
3764
- const config$3 = adminSdk.defineRouteConfig({
3765
- label: "Integrációk állapota",
3766
- icon: Newspaper,
3767
- rank: 38
3768
- });
3769
- const backendUrl$3 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
3770
- const adminBase$2 = (__BASE__ || "/app").replace(/\/+$/, "");
3771
- const apiUrl$3 = (path) => `${backendUrl$3}${path.startsWith("/") ? path : `/${path}`}`;
3772
- const CATEGORY_LABELS = {
3773
- infrastructure: "Infrastruktúra",
3774
- security: "Biztonság",
3775
- store: "Storefront alapok",
3776
- commerce: "Kereskedelem",
3777
- communication: "Kommunikáció",
3778
- compliance: "Jogi megfelelés",
3779
- marketing: "Mérés és SEO"
3780
- };
3781
- const ProductionReadinessPage = () => {
3782
- const [report, setReport] = React.useState(null);
3783
- const [loading, setLoading] = React.useState(true);
3784
- const [error, setError] = React.useState(null);
3785
- const load = React.useCallback(async () => {
3786
- setLoading(true);
3787
- setError(null);
3788
- try {
3789
- const response = await fetch(apiUrl$3("/admin/production-readiness"), {
3790
- credentials: "include"
3791
- });
3792
- const payload = await response.json().catch(() => ({}));
3793
- if (!response.ok) {
3794
- throw new Error(payload.message || "Az élesítési ellenőrzés nem tölthető be.");
3795
- }
3796
- setReport(payload);
3797
- } catch (cause) {
3798
- setReport(null);
3799
- setError(
3800
- cause instanceof Error ? cause.message : "Az élesítési ellenőrzés nem tölthető be."
3801
- );
3802
- } finally {
3803
- setLoading(false);
3804
- }
3805
- }, []);
3806
- React.useEffect(() => {
3807
- void load();
3808
- }, [load]);
3809
- const groups = React.useMemo(() => {
3810
- const result = /* @__PURE__ */ new Map();
3811
- for (const item of report?.checks || []) {
3812
- result.set(item.category, [...result.get(item.category) || [], item]);
3813
- }
3814
- return Array.from(result.entries());
3815
- }, [report]);
3816
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
3817
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4 border-b px-6 py-5", children: [
3818
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3819
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Production readiness" }),
3820
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-ui-fg-subtle", children: "Élesítés előtti runtime ellenőrzés. A felület státuszokat mutat, secret értéket nem ad vissza." })
3821
- ] }),
3822
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", isLoading: loading, onClick: () => void load(), children: "Újraellenőrzés" })
3823
- ] }),
3824
- report ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3825
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 border-b p-6 sm:grid-cols-2 lg:grid-cols-4", children: [
3826
- /* @__PURE__ */ jsxRuntime.jsx(Summary, { label: "Kész", value: report.score.ready, tone: "ready" }),
3827
- /* @__PURE__ */ jsxRuntime.jsx(Summary, { label: "Figyelmeztetés", value: report.score.warning, tone: "warning" }),
3828
- /* @__PURE__ */ jsxRuntime.jsx(Summary, { label: "Blokkoló", value: report.score.blocked, tone: "blocked" }),
3829
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4", children: [
3830
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Élesítési döntés" }),
3831
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: report.ready ? "mt-1 text-xl font-semibold text-ui-fg-success" : "mt-1 text-xl font-semibold text-ui-fg-error", children: report.ready ? "Élesíthető" : "Még nem élesíthető" }),
3832
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "mt-1 text-ui-fg-subtle", children: report.environment === "production" ? "Production szabályok" : "Nem-production szabályok" })
3833
- ] })
3834
- ] }),
3835
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b px-6 py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
3836
- "Utolsó ellenőrzés: ",
3837
- formatDate$1(report.generated_at),
3838
- ". A sárga elemek nem blokkolják az élesítést, a piros elemek igen."
3839
- ] }) }),
3840
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-6 p-6", children: groups.map(([category, checks]) => /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "overflow-hidden rounded-lg border", children: [
3841
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b bg-ui-bg-subtle px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: CATEGORY_LABELS[category] || category }) }),
3842
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: checks.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 p-4 md:grid-cols-[180px_140px_minmax(0,1fr)_auto] md:items-center", children: [
3843
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: item.label }),
3844
- /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: item.status }),
3845
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: item.message }),
3846
- item.action_href && item.action_label ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: normalizeAdminHref(item.action_href), children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: item.action_label }) }) : /* @__PURE__ */ jsxRuntime.jsx("span", {})
3847
- ] }, item.key)) })
3848
- ] }, category)) })
3849
- ] }) : null,
3850
- loading && !report ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Élesítési feltételek ellenőrzése…" }) }) : null,
3851
- error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "m-6 rounded-lg border border-ui-border-error p-4", children: [
3852
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-error", children: "Az ellenőrzés nem futott le." }),
3853
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: error })
3854
- ] }) : null
3855
- ] });
3856
- };
3857
- function StatusBadge({ status }) {
3858
- const label = status === "ready" ? "Kész" : status === "warning" ? "Figyelmeztetés" : "Blokkoló";
3859
- const className = status === "ready" ? "font-medium text-ui-fg-success" : status === "warning" ? "font-medium text-ui-fg-warning" : "font-medium text-ui-fg-error";
3860
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { className, children: [
3861
- status === "ready" ? "✅" : status === "warning" ? "🟡" : "🔴",
3862
- " ",
3863
- label
3864
- ] });
3865
- }
3866
- function Summary({ label, value, tone }) {
3867
- const valueClass = tone === "ready" ? "text-ui-fg-success" : tone === "warning" ? "text-ui-fg-warning" : "text-ui-fg-error";
3868
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4", children: [
3869
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: label }),
3870
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `mt-1 text-3xl font-semibold ${valueClass}`, children: value })
3871
- ] });
3872
- }
3873
- function normalizeAdminHref(path) {
3874
- if (!path.startsWith("/app")) return path;
3875
- return `${adminBase$2}${path.slice(4)}`;
3876
- }
3877
- function formatDate$1(value) {
3878
- const date = new Date(value);
3879
- if (Number.isNaN(date.getTime())) return "—";
3880
- return new Intl.DateTimeFormat("hu-HU", { dateStyle: "short", timeStyle: "medium" }).format(date);
3881
- }
3882
- const config$2 = adminSdk.defineRouteConfig({
3883
- label: "Production readiness",
3884
- rank: 39
3885
- });
3886
- const backendUrl$2 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
3887
- const adminBase$1 = (__BASE__ || "/app").replace(/\/+$/, "");
3888
- const apiUrl$2 = (path) => `${backendUrl$2}${path.startsWith("/") ? path : `/${path}`}`;
3889
- const CATEGORY_ORDER = [
3890
- "payment",
3891
- "invoicing",
3892
- "shipping",
3893
- "notification",
3894
- "legal",
3895
- "marketing"
3896
- ];
3897
- const CATEGORY_LABEL = {
3898
- payment: "Fizetés",
3899
- invoicing: "Számlázás",
3900
- shipping: "Szállítás",
3901
- notification: "Értesítés",
3902
- legal: "Jogi integráció",
3903
- marketing: "Marketing"
3568
+ return new Intl.DateTimeFormat("hu-HU", {
3569
+ dateStyle: "short",
3570
+ timeStyle: "short"
3571
+ }).format(date);
3572
+ }
3573
+ const config$4 = adminSdk.defineRouteConfig({
3574
+ label: "Integrációk állapota",
3575
+ icon: Newspaper,
3576
+ rank: 38
3577
+ });
3578
+ const defaults = {
3579
+ GOOGLE_MERCHANT_COUNTRY_CODE: "HU",
3580
+ GOOGLE_MERCHANT_CURRENCY_CODE: "HUF",
3581
+ GOOGLE_MERCHANT_STOREFRONT_URL: "",
3582
+ GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE: "/{country_code}/products/{handle}?v_id={variant_id}",
3583
+ GOOGLE_MERCHANT_SALES_CHANNEL_ID: "",
3584
+ GOOGLE_MERCHANT_DEFAULT_BRAND: "",
3585
+ GOOGLE_MERCHANT_DEFAULT_CONDITION: "new",
3586
+ GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK: true,
3587
+ GOOGLE_MERCHANT_FEED_TITLE: "",
3588
+ GOOGLE_MERCHANT_FEED_DESCRIPTION: ""
3904
3589
  };
3905
- const IntegrationsPage = () => {
3906
- const [items, setItems] = React.useState([]);
3907
- const [selectedKey, setSelectedKey] = React.useState(null);
3908
- const [draft, setDraft] = React.useState({});
3909
- const [clearSecrets, setClearSecrets] = React.useState([]);
3590
+ const backendUrl$4 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
3591
+ const apiUrl$4 = (path) => `${backendUrl$4}${path.startsWith("/") ? path : `/${path}`}`;
3592
+ const GoogleMerchantPage = () => {
3910
3593
  const [enabled, setEnabled] = React.useState(false);
3594
+ const [draft, setDraft] = React.useState(defaults);
3911
3595
  const [loading, setLoading] = React.useState(true);
3912
3596
  const [saving, setSaving] = React.useState(false);
3913
- const [testing, setTesting] = React.useState(false);
3914
- const [restartNotice, setRestartNotice] = React.useState(false);
3915
- const visibleItems = React.useMemo(
3916
- () => items.filter((item) => CATEGORY_ORDER.includes(item.category)),
3917
- [items]
3918
- );
3919
- const selected = React.useMemo(
3920
- () => visibleItems.find((item) => item.key === selectedKey) || null,
3921
- [visibleItems, selectedKey]
3922
- );
3923
- const hasUnsavedChanges = React.useMemo(() => {
3924
- if (!selected) return false;
3925
- if (enabled !== selected.enabled || clearSecrets.length > 0) return true;
3926
- return selected.fields.some((field) => {
3927
- const value = draft[field.name];
3928
- if (field.type === "secret") {
3929
- return Boolean(String(value || "").trim());
3930
- }
3931
- if (field.type === "boolean") {
3932
- return Boolean(value) !== (selected.values[field.name] === "true");
3933
- }
3934
- return String(value || "").trim() !== String(selected.values[field.name] || "").trim();
3935
- });
3936
- }, [selected, enabled, draft, clearSecrets]);
3597
+ const [previewing, setPreviewing] = React.useState(false);
3598
+ const [preview, setPreview] = React.useState(null);
3599
+ const update = (key, value) => {
3600
+ setDraft((current) => ({ ...current, [key]: value }));
3601
+ };
3937
3602
  const load = React.useCallback(async () => {
3938
3603
  setLoading(true);
3939
3604
  try {
3940
- const response = await fetch(apiUrl$2("/admin/integrations"), {
3605
+ const response = await fetch(apiUrl$4("/admin/integrations/google-merchant"), {
3941
3606
  credentials: "include"
3942
3607
  });
3943
3608
  const data = await response.json().catch(() => ({}));
3944
- if (!response.ok) {
3945
- throw new Error(data.message || "Az integrációk nem tölthetők be.");
3609
+ if (!response.ok || !data.integration) {
3610
+ throw new Error(data.message || "A Google Merchant beállítások nem tölthetők be.");
3946
3611
  }
3947
- const integrations = data.integrations || [];
3948
- setItems(integrations);
3949
- const eligible = integrations.filter(
3950
- (item) => CATEGORY_ORDER.includes(item.category)
3951
- );
3952
- setSelectedKey(
3953
- (current) => current && eligible.some((item) => item.key === current) ? current : eligible[0]?.key || null
3954
- );
3612
+ const values = data.integration.values || {};
3613
+ setEnabled(Boolean(data.integration.enabled));
3614
+ setDraft({
3615
+ GOOGLE_MERCHANT_COUNTRY_CODE: values.GOOGLE_MERCHANT_COUNTRY_CODE || "HU",
3616
+ GOOGLE_MERCHANT_CURRENCY_CODE: values.GOOGLE_MERCHANT_CURRENCY_CODE || "HUF",
3617
+ GOOGLE_MERCHANT_STOREFRONT_URL: values.GOOGLE_MERCHANT_STOREFRONT_URL || "",
3618
+ GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE: values.GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE || "/{country_code}/products/{handle}?v_id={variant_id}",
3619
+ GOOGLE_MERCHANT_SALES_CHANNEL_ID: values.GOOGLE_MERCHANT_SALES_CHANNEL_ID || "",
3620
+ GOOGLE_MERCHANT_DEFAULT_BRAND: values.GOOGLE_MERCHANT_DEFAULT_BRAND || "",
3621
+ GOOGLE_MERCHANT_DEFAULT_CONDITION: values.GOOGLE_MERCHANT_DEFAULT_CONDITION || "new",
3622
+ GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK: values.GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK !== "false",
3623
+ GOOGLE_MERCHANT_FEED_TITLE: values.GOOGLE_MERCHANT_FEED_TITLE || "",
3624
+ GOOGLE_MERCHANT_FEED_DESCRIPTION: values.GOOGLE_MERCHANT_FEED_DESCRIPTION || ""
3625
+ });
3955
3626
  } catch (error) {
3956
- ui.toast.error(
3957
- error instanceof Error ? error.message : "Az integrációk nem tölthetők be."
3958
- );
3627
+ ui.toast.error(error instanceof Error ? error.message : "Betöltési hiba.");
3959
3628
  } finally {
3960
3629
  setLoading(false);
3961
3630
  }
@@ -3963,641 +3632,972 @@ const IntegrationsPage = () => {
3963
3632
  React.useEffect(() => {
3964
3633
  void load();
3965
3634
  }, [load]);
3966
- React.useEffect(() => {
3967
- if (!selected) return;
3968
- const next = {};
3969
- for (const field of selected.fields) {
3970
- if (field.type === "secret") next[field.name] = "";
3971
- else if (field.type === "boolean") {
3972
- next[field.name] = selected.values[field.name] === "true";
3973
- } else {
3974
- next[field.name] = selected.values[field.name] || "";
3975
- }
3976
- }
3977
- setDraft(next);
3978
- setClearSecrets([]);
3979
- setEnabled(selected.enabled);
3980
- setRestartNotice(false);
3981
- }, [selected?.key, selected?.values, selected?.enabled]);
3982
3635
  const save = async () => {
3983
- if (!selected) return;
3984
3636
  setSaving(true);
3985
3637
  try {
3986
- const values = {};
3987
- for (const field of selected.fields) {
3988
- const value = draft[field.name];
3989
- if (field.type === "secret" && !String(value || "").trim()) continue;
3990
- values[field.name] = value;
3991
- }
3992
- const response = await fetch(apiUrl$2(`/admin/integrations/${selected.key}`), {
3638
+ const response = await fetch(apiUrl$4("/admin/integrations/google-merchant"), {
3993
3639
  method: "POST",
3994
3640
  credentials: "include",
3995
3641
  headers: { "Content-Type": "application/json" },
3996
- body: JSON.stringify({
3997
- enabled,
3998
- values,
3999
- clear_secrets: clearSecrets
4000
- })
3642
+ body: JSON.stringify({ enabled, values: draft })
3643
+ });
3644
+ const data = await response.json().catch(() => ({}));
3645
+ if (!response.ok) throw new Error(data.message || "A mentés nem sikerült.");
3646
+ ui.toast.success(data.message || "Google Merchant beállítások mentve.");
3647
+ setPreview(null);
3648
+ await load();
3649
+ } catch (error) {
3650
+ ui.toast.error(error instanceof Error ? error.message : "A mentés nem sikerült.");
3651
+ } finally {
3652
+ setSaving(false);
3653
+ }
3654
+ };
3655
+ const runPreview = async () => {
3656
+ setPreviewing(true);
3657
+ try {
3658
+ const response = await fetch(apiUrl$4("/admin/google-merchant/preview"), {
3659
+ credentials: "include"
3660
+ });
3661
+ const data = await response.json().catch(() => ({}));
3662
+ if (!response.ok) throw new Error(data.message || "A feed nem generálható.");
3663
+ setPreview(data);
3664
+ ui.toast.success("A Merchant feed ellenőrzése elkészült.");
3665
+ } catch (error) {
3666
+ ui.toast.error(error instanceof Error ? error.message : "A feed nem generálható.");
3667
+ } finally {
3668
+ setPreviewing(false);
3669
+ }
3670
+ };
3671
+ const feedUrl = apiUrl$4("/feeds/google-merchant.xml");
3672
+ const d = preview?.diagnostics;
3673
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
3674
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4 border-b px-6 py-4", children: [
3675
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3676
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Google Merchant Center" }),
3677
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Google RSS 2.0 feed a Medusa termék-, variant-, régiós ár-, ÁFA- és készletadataiból." })
3678
+ ] }),
3679
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
3680
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: enabled, onCheckedChange: setEnabled, disabled: loading }),
3681
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: enabled ? "Bekapcsolva" : "Kikapcsolva" })
3682
+ ] })
3683
+ ] }),
3684
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 border-b px-6 py-5 md:grid-cols-2", children: [
3685
+ /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Célország", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_COUNTRY_CODE, onChange: (e) => update("GOOGLE_MERCHANT_COUNTRY_CODE", e.target.value.toUpperCase()), placeholder: "HU" }) }),
3686
+ /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Pénznem", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_CURRENCY_CODE, onChange: (e) => update("GOOGLE_MERCHANT_CURRENCY_CODE", e.target.value.toUpperCase()), placeholder: "HUF" }) }),
3687
+ /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Storefront URL", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_STOREFRONT_URL, onChange: (e) => update("GOOGLE_MERCHANT_STOREFRONT_URL", e.target.value), placeholder: "https://shop.hu (üresen STOREFRONT_URL)" }) }),
3688
+ /* @__PURE__ */ jsxRuntime.jsxs(Field$1, { label: "Termék URL sablon", children: [
3689
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE, onChange: (e) => update("GOOGLE_MERCHANT_PRODUCT_PATH_TEMPLATE", e.target.value) }),
3690
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-xs", children: [
3691
+ "Helyőrzők: ",
3692
+ "{country_code}",
3693
+ ", ",
3694
+ "{currency_code}",
3695
+ ", ",
3696
+ "{handle}",
3697
+ ", ",
3698
+ "{variant_id}",
3699
+ "."
3700
+ ] })
3701
+ ] }),
3702
+ /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Sales Channel ID", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_SALES_CHANNEL_ID, onChange: (e) => update("GOOGLE_MERCHANT_SALES_CHANNEL_ID", e.target.value), placeholder: "sc_... (opcionális)" }) }),
3703
+ /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Alapértelmezett márka", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_DEFAULT_BRAND, onChange: (e) => update("GOOGLE_MERCHANT_DEFAULT_BRAND", e.target.value) }) }),
3704
+ /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Alapértelmezett állapot", children: /* @__PURE__ */ jsxRuntime.jsxs("select", { className: "h-8 rounded-md border bg-ui-bg-field px-2 text-sm", value: draft.GOOGLE_MERCHANT_DEFAULT_CONDITION, onChange: (e) => update("GOOGLE_MERCHANT_DEFAULT_CONDITION", e.target.value), children: [
3705
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "new", children: "Új" }),
3706
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "refurbished", children: "Felújított" }),
3707
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "used", children: "Használt" })
3708
+ ] }) }),
3709
+ /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Készlethiányos variantok", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center gap-2", children: [
3710
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: draft.GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK, onCheckedChange: (value) => update("GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK", value) }),
3711
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: draft.GOOGLE_MERCHANT_INCLUDE_OUT_OF_STOCK ? "Benne maradnak" : "Kimaradnak" })
3712
+ ] }) }),
3713
+ /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Feed neve", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_FEED_TITLE, onChange: (e) => update("GOOGLE_MERCHANT_FEED_TITLE", e.target.value), placeholder: "Üresen automatikus" }) }),
3714
+ /* @__PURE__ */ jsxRuntime.jsx(Field$1, { label: "Feed leírása", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Input, { value: draft.GOOGLE_MERCHANT_FEED_DESCRIPTION, onChange: (e) => update("GOOGLE_MERCHANT_FEED_DESCRIPTION", e.target.value), placeholder: "Üresen automatikus" }) })
3715
+ ] }),
3716
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b px-6 py-4", children: [
3717
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3718
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: "Publikus feed URL" }),
3719
+ /* @__PURE__ */ jsxRuntime.jsx("a", { className: "break-all text-ui-fg-interactive hover:underline", href: feedUrl, target: "_blank", rel: "noreferrer", children: feedUrl })
3720
+ ] }),
3721
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
3722
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", isLoading: previewing, disabled: !enabled || loading || saving || previewing, onClick: () => void runPreview(), children: "Feed ellenőrzése" }),
3723
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { isLoading: saving, disabled: loading || saving || previewing, onClick: () => void save(), children: "Mentés" })
3724
+ ] })
3725
+ ] }),
3726
+ d ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3727
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 border-b px-6 py-5 sm:grid-cols-2 lg:grid-cols-4", children: [
3728
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Termékek", value: d.productsScanned }),
3729
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Variantok", value: d.variantsScanned }),
3730
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Feed tételek", value: d.itemsEmitted }),
3731
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Hiányzó identifier", value: d.itemsMissingIdentifiers }),
3732
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Nincs ár", value: d.skippedNoPrice }),
3733
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Nincs kép", value: d.skippedNoImage }),
3734
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Nincs sales channel", value: d.skippedNoSalesChannel + d.skippedNotInSalesChannel }),
3735
+ /* @__PURE__ */ jsxRuntime.jsx(Stat, { label: "Érvénytelen GTIN", value: d.invalidGtinsIgnored })
3736
+ ] }),
3737
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-left text-sm", children: [
3738
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "border-b bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
3739
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "ID" }),
3740
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Termék" }),
3741
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Ár" }),
3742
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Készlet" }),
3743
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Identifier" })
3744
+ ] }) }),
3745
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: (preview?.sample || []).map((item) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b", children: [
3746
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: item.id }) }),
3747
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.link, target: "_blank", rel: "noreferrer", className: "text-ui-fg-interactive hover:underline", children: item.title }) }),
3748
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.salePrice ? `${item.salePrice} (${item.price})` : item.price }),
3749
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.availability }),
3750
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: item.gtin || item.mpn || item.brand || "—" })
3751
+ ] }, item.id)) })
3752
+ ] }) })
3753
+ ] }) : null
3754
+ ] });
3755
+ };
3756
+ function Field$1({ label, children }) {
3757
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
3758
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: label }),
3759
+ children
3760
+ ] });
3761
+ }
3762
+ function Stat({ label, value }) {
3763
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-3", children: [
3764
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: label }),
3765
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: value })
3766
+ ] });
3767
+ }
3768
+ const config$3 = adminSdk.defineRouteConfig({ label: "Google Merchant", rank: 41 });
3769
+ const backendUrl$3 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
3770
+ const adminBase$2 = (__BASE__ || "/app").replace(/\/+$/, "");
3771
+ function apiUrl$3(path) {
3772
+ return `${backendUrl$3}${path.startsWith("/") ? path : `/${path}`}`;
3773
+ }
3774
+ const IntegrationOperationsPage = () => {
3775
+ const [operations, setOperations] = React.useState([]);
3776
+ const [summary, setSummary] = React.useState({
3777
+ failed: 0,
3778
+ needsReconciliation: 0,
3779
+ processing: 0
3780
+ });
3781
+ const [count, setCount] = React.useState(0);
3782
+ const [status, setStatus] = React.useState("problems");
3783
+ const [provider, setProvider] = React.useState("all");
3784
+ const [query, setQuery] = React.useState("");
3785
+ const [loading, setLoading] = React.useState(false);
3786
+ const [actionLoading, setActionLoading] = React.useState(null);
3787
+ const [loadError, setLoadError] = React.useState(null);
3788
+ const load = React.useCallback(async () => {
3789
+ setLoading(true);
3790
+ setLoadError(null);
3791
+ try {
3792
+ const params = new URLSearchParams({
3793
+ limit: "100",
3794
+ status
4001
3795
  });
4002
- const data = await response.json().catch(() => ({}));
3796
+ if (provider !== "all") params.set("provider", provider);
3797
+ const response = await fetch(
3798
+ apiUrl$3(`/admin/integration-operations?${params.toString()}`),
3799
+ { credentials: "include" }
3800
+ );
3801
+ const payload = await response.json().catch(() => ({}));
4003
3802
  if (!response.ok) {
4004
- throw new Error(data.message || "A mentés nem sikerült.");
3803
+ throw new Error(
3804
+ payload.message || "Az integrációs műveletek nem tölthetők be."
3805
+ );
4005
3806
  }
4006
- if (data.integration) replaceIntegration(data.integration);
4007
- setClearSecrets([]);
4008
- setRestartNotice(Boolean(data.integration?.restart_required));
4009
- ui.toast.success(data.message || "Beállítások mentve.");
3807
+ setOperations(payload.operations || []);
3808
+ setCount(payload.count || 0);
3809
+ setSummary(
3810
+ payload.summary || {
3811
+ failed: 0,
3812
+ needsReconciliation: 0,
3813
+ processing: 0
3814
+ }
3815
+ );
4010
3816
  } catch (error) {
4011
- ui.toast.error(error instanceof Error ? error.message : "A mentés nem sikerült.");
3817
+ setOperations([]);
3818
+ setLoadError(
3819
+ error instanceof Error ? error.message : "Az integrációs műveletek nem tölthetők be."
3820
+ );
4012
3821
  } finally {
4013
- setSaving(false);
3822
+ setLoading(false);
4014
3823
  }
4015
- };
4016
- const test = async () => {
4017
- if (!selected || hasUnsavedChanges) return;
4018
- setTesting(true);
4019
- try {
4020
- const response = await fetch(apiUrl$2(`/admin/integrations/${selected.key}/test`), {
4021
- method: "POST",
4022
- credentials: "include"
4023
- });
4024
- const data = await response.json().catch(() => ({}));
4025
- if (data.integration) replaceIntegration(data.integration);
4026
- if (!response.ok || !data.ok) {
4027
- throw new Error(data.message || "A kapcsolat tesztelése nem sikerült.");
3824
+ }, [provider, status]);
3825
+ React.useEffect(() => {
3826
+ void load();
3827
+ }, [load]);
3828
+ const visible = React.useMemo(() => {
3829
+ const value = query.trim().toLowerCase();
3830
+ if (!value) return operations;
3831
+ return operations.filter(
3832
+ (operation) => [
3833
+ operation.provider,
3834
+ operation.operation_type,
3835
+ operation.entity_type,
3836
+ operation.entity_id,
3837
+ operation.external_id,
3838
+ operation.error_summary
3839
+ ].some(
3840
+ (item) => String(item || "").toLowerCase().includes(value)
3841
+ )
3842
+ );
3843
+ }, [operations, query]);
3844
+ const reconcile = async (operation, decision) => {
3845
+ const confirmed = confirm(
3846
+ decision === "confirmed_succeeded" ? "Megerősíted, hogy a külső szolgáltatónál ez a művelet valóban sikeresen megtörtént?" : "Megerősíted, hogy a külső szolgáltatónál ez a művelet biztosan NEM történt meg? Ez engedélyezi a normál művelet későbbi újrapróbálását."
3847
+ );
3848
+ if (!confirmed) return;
3849
+ let externalId = operation.external_id || void 0;
3850
+ if (decision === "confirmed_succeeded" && !externalId) {
3851
+ externalId = prompt(
3852
+ "Add meg a külső szolgáltató azonosítóját (pl. Billingo document ID vagy shipment ID):"
3853
+ )?.trim() || void 0;
3854
+ if (!externalId) {
3855
+ ui.toast.error("A sikeres lezáráshoz külső azonosító szükséges.");
3856
+ return;
4028
3857
  }
4029
- if (data.verified) {
4030
- ui.toast.success(data.message || "Kapcsolat ellenőrizve.");
4031
- } else {
4032
- ui.toast.success(data.message || "Részleges ellenőrzés sikeres.");
3858
+ }
3859
+ const note = prompt(
3860
+ "Opcionális megjegyzés az egyeztetéshez:",
3861
+ ""
3862
+ )?.trim();
3863
+ setActionLoading(operation.id);
3864
+ try {
3865
+ const response = await fetch(
3866
+ apiUrl$3(`/admin/integration-operations/${operation.id}/reconcile`),
3867
+ {
3868
+ method: "POST",
3869
+ credentials: "include",
3870
+ headers: { "Content-Type": "application/json" },
3871
+ body: JSON.stringify({ decision, externalId, note })
3872
+ }
3873
+ );
3874
+ const payload = await response.json().catch(() => ({}));
3875
+ if (!response.ok) {
3876
+ throw new Error(
3877
+ payload.message || "Az egyeztetés lezárása nem sikerült."
3878
+ );
4033
3879
  }
3880
+ ui.toast.success(
3881
+ decision === "confirmed_succeeded" ? "A művelet sikeresként lezárva." : "A művelet újrapróbálhatóvá téve."
3882
+ );
3883
+ await load();
4034
3884
  } catch (error) {
4035
3885
  ui.toast.error(
4036
- error instanceof Error ? error.message : "A kapcsolat tesztelése nem sikerült."
3886
+ error instanceof Error ? error.message : "Az egyeztetés lezárása nem sikerült."
4037
3887
  );
4038
- void load();
4039
3888
  } finally {
4040
- setTesting(false);
3889
+ setActionLoading(null);
4041
3890
  }
4042
3891
  };
4043
- const replaceIntegration = (integration) => {
4044
- setItems(
4045
- (current) => current.map(
4046
- (item) => item.key === integration.key ? integration : item
4047
- )
4048
- );
4049
- };
4050
- const detailHref = selected ? detailPageForIntegration(selected.key) : null;
4051
- const connection = selected ? connectionPresentation(selected) : null;
4052
3892
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
4053
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b px-6 py-5", children: [
4054
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Integrációk" }),
4055
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-ui-fg-subtle", children: "Külső szolgáltatások és provider kapcsolatok központi beállítása. A secret értékek nem kerülnek vissza a böngészőbe, a provider-hozzárendelés pedig továbbra is a HSZRDM Commerce Admin natív felületein történik." })
3893
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 border-b px-6 py-4", children: [
3894
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3895
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Integrációs műveletek" }),
3896
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Külső API műveletek, hibák és kézi reconciliation kezelése." })
3897
+ ] }),
3898
+ /* @__PURE__ */ jsxRuntime.jsx(
3899
+ ui.Button,
3900
+ {
3901
+ variant: "secondary",
3902
+ isLoading: loading,
3903
+ onClick: () => void load(),
3904
+ children: "Frissítés"
3905
+ }
3906
+ )
4056
3907
  ] }),
4057
- loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Betöltés…" }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid lg:grid-cols-[320px_minmax(0,1fr)]", children: [
4058
- /* @__PURE__ */ jsxRuntime.jsx("aside", { className: "border-r p-4", children: CATEGORY_ORDER.map((category) => {
4059
- const categoryItems = visibleItems.filter(
4060
- (item) => item.category === category
4061
- );
4062
- if (!categoryItems.length) return null;
4063
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 last:mb-0", children: [
4064
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-2 px-1 text-xs font-medium uppercase tracking-wide text-ui-fg-muted", children: CATEGORY_LABEL[category] }),
4065
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: categoryItems.map((item) => {
4066
- const itemConnection = connectionPresentation(item);
4067
- return /* @__PURE__ */ jsxRuntime.jsxs(
4068
- "button",
4069
- {
4070
- type: "button",
4071
- onClick: () => setSelectedKey(item.key),
4072
- className: `rounded-lg border p-3 text-left transition-colors hover:bg-ui-bg-subtle ${selectedKey === item.key ? "bg-ui-bg-subtle" : "bg-ui-bg-base"}`,
4073
- children: [
4074
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
4075
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: item.label }),
4076
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: item.enabled ? "text-xs font-medium text-ui-fg-success" : "text-xs text-ui-fg-muted", children: item.enabled ? "Aktív" : "Kikapcsolva" })
4077
- ] }),
4078
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 text-xs text-ui-fg-subtle", children: [
4079
- item.configured ? "Konfigurálva" : "Beállítás szükséges",
4080
- " · ",
4081
- itemConnection.label
4082
- ] })
4083
- ]
4084
- },
4085
- item.key
4086
- );
4087
- }) })
4088
- ] }, category);
4089
- }) }),
4090
- /* @__PURE__ */ jsxRuntime.jsx("main", { className: "min-w-0 p-6", children: selected && connection ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-w-4xl flex-col gap-6", children: [
4091
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [
4092
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
4093
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: selected.label }),
4094
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-ui-fg-subtle", children: selected.description })
3908
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 border-b px-6 py-4 md:grid-cols-3", children: [
3909
+ /* @__PURE__ */ jsxRuntime.jsx(
3910
+ SummaryCard,
3911
+ {
3912
+ label: "Egyeztetést igényel",
3913
+ value: summary.needsReconciliation,
3914
+ danger: summary.needsReconciliation > 0
3915
+ }
3916
+ ),
3917
+ /* @__PURE__ */ jsxRuntime.jsx(
3918
+ SummaryCard,
3919
+ {
3920
+ label: "Sikertelen",
3921
+ value: summary.failed,
3922
+ danger: summary.failed > 0
3923
+ }
3924
+ ),
3925
+ /* @__PURE__ */ jsxRuntime.jsx(SummaryCard, { label: "Folyamatban", value: summary.processing })
3926
+ ] }),
3927
+ summary.needsReconciliation > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-ui-border-warning bg-ui-bg-base px-6 py-4", children: [
3928
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "font-medium text-ui-fg-warning", children: [
3929
+ summary.needsReconciliation,
3930
+ " műveletnél nem dönthető el automatikusan, hogy a külső szolgáltató végrehajtotta-e a kérést."
3931
+ ] }),
3932
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Ellenőrizd a szolgáltatónál, majd csak ezután jelöld sikeresnek vagy újrapróbálhatónak. Így elkerülhető a duplikált számla, csomag vagy fizetés." })
3933
+ ] }) : null,
3934
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-end gap-3 border-b px-6 py-4", children: [
3935
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
3936
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Státusz" }),
3937
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: status, onValueChange: setStatus, children: [
3938
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { className: "w-52", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, {}) }),
3939
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
3940
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "problems", children: "Hibák + egyeztetés" }),
3941
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "needs_reconciliation", children: "Egyeztetést igényel" }),
3942
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "failed", children: "Sikertelen" }),
3943
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "processing", children: "Folyamatban" }),
3944
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "pending", children: "Várakozik" }),
3945
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "succeeded", children: "Sikeres" }),
3946
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "all", children: "Összes" })
3947
+ ] })
3948
+ ] })
3949
+ ] }),
3950
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
3951
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Provider" }),
3952
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: provider, onValueChange: setProvider, children: [
3953
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { className: "w-44", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, {}) }),
3954
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
3955
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "all", children: "Összes" }),
3956
+ [
3957
+ "stripe",
3958
+ "simplepay",
3959
+ "billingo",
3960
+ "foxpost",
3961
+ "gls",
3962
+ "packeta",
3963
+ "dpd",
3964
+ "mpl"
3965
+ ].map((item) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: item, children: providerLabel(item) }, item))
3966
+ ] })
3967
+ ] })
3968
+ ] }),
3969
+ /* @__PURE__ */ jsxRuntime.jsx(
3970
+ ui.Input,
3971
+ {
3972
+ className: "max-w-sm",
3973
+ value: query,
3974
+ onChange: (event) => setQuery(event.target.value),
3975
+ placeholder: "Keresés művelet, rendelés, külső ID vagy hiba alapján"
3976
+ }
3977
+ ),
3978
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
3979
+ count,
3980
+ " találat"
3981
+ ] })
3982
+ ] }),
3983
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-x-auto", children: [
3984
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-left text-sm", children: [
3985
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "border-b bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
3986
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Státusz" }),
3987
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Provider / művelet" }),
3988
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Entitás" }),
3989
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Külső ID" }),
3990
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Próbálkozás" }),
3991
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Hiba / idő" }),
3992
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Műveletek" })
3993
+ ] }) }),
3994
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: visible.map((operation) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b align-top", children: [
3995
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(StatusText, { status: operation.status }) }),
3996
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
3997
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: providerLabel(operation.provider) }),
3998
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: operation.operation_type })
4095
3999
  ] }),
4096
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end gap-1", children: [
4097
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
4098
- /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: enabled, onCheckedChange: setEnabled }),
4099
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: enabled ? "Bekapcsolva" : "Kikapcsolva" })
4100
- ] }),
4101
- hasUnsavedChanges ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-warning", children: "Nem mentett módosítások" }) : null
4102
- ] })
4103
- ] }),
4104
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-4", children: [
4105
- /* @__PURE__ */ jsxRuntime.jsx(StatusBox, { label: "Telepítve", value: selected.installed ? "Igen" : "Nem", state: selected.installed ? "success" : "neutral" }),
4106
- /* @__PURE__ */ jsxRuntime.jsx(StatusBox, { label: "Konfigurálva", value: selected.configured ? "Igen" : "Nem", state: selected.configured ? "success" : "neutral" }),
4107
- /* @__PURE__ */ jsxRuntime.jsx(StatusBox, { label: "Aktív", value: selected.enabled ? "Igen" : "Nem", state: selected.enabled ? "success" : "neutral" }),
4108
- /* @__PURE__ */ jsxRuntime.jsx(StatusBox, { label: "Kapcsolat", value: connection.label, state: connection.state })
4109
- ] }),
4110
- selected.category === "shipping" ? /* @__PURE__ */ jsxRuntime.jsx(Notice, { title: "HSZRDM Commerce provider hozzárendelés", children: "Mentés és backend restart után a providert a HSZRDM Commerce Admin Settings → Locations & Shipping alatt rendeld locationhöz." }) : null,
4111
- selected.category === "legal" ? /* @__PURE__ */ jsxRuntime.jsx(Notice, { title: "Fogyasztó Barát EMBED", children: "Ez csak az opcionális Fogyasztó Barát embed konfigurációja. A PDF-ek és checkout elfogadások a főoldali Jogi dokumentumok menüpontban kezelhetők." }) : null,
4112
- selected.category === "marketing" ? /* @__PURE__ */ jsxRuntime.jsx(Notice, { title: "Marketing feed", children: "Itt az integráció alapbeállításait módosítod. A feed előnézet, diagnosztika és publikus URL a főoldali Marketing menüpontban található." }) : null,
4113
- restartNotice ? /* @__PURE__ */ jsxRuntime.jsx(Notice, { title: "Újraindítás szükséges", children: "A beállítások elmentve. Indítsd újra a HSZRDM Commerce backendet az új provider-konfiguráció betöltéséhez." }) : null,
4114
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4115
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Beállítások" }),
4116
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 grid gap-4 sm:grid-cols-2", children: selected.fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
4117
- FieldEditor,
4118
- {
4119
- field,
4120
- value: draft[field.name],
4121
- hasSecret: Boolean(selected.secrets[field.name]),
4122
- clearPending: clearSecrets.includes(field.name),
4123
- onChange: (value) => {
4124
- setDraft((current) => ({ ...current, [field.name]: value }));
4125
- if (field.type === "secret" && String(value || "").trim()) {
4126
- setClearSecrets(
4127
- (current) => current.filter((name) => name !== field.name)
4128
- );
4129
- }
4130
- },
4131
- onClearSecret: () => {
4132
- setDraft((current) => ({ ...current, [field.name]: "" }));
4133
- setClearSecrets(
4134
- (current) => current.includes(field.name) ? current : [...current, field.name]
4135
- );
4136
- },
4137
- onRestoreSecret: () => setClearSecrets(
4138
- (current) => current.filter((name) => name !== field.name)
4139
- )
4140
- },
4141
- field.name
4142
- )) })
4143
- ] }),
4144
- selected.last_test_message ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4", children: [
4145
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
4146
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: "Utolsó kapcsolat-teszt" }),
4147
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: connectionTextClass(connection.state), children: connection.label })
4000
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
4001
+ operation.entity_type === "order" ? /* @__PURE__ */ jsxRuntime.jsx(
4002
+ "a",
4003
+ {
4004
+ href: `${adminBase$2}/orders/${operation.entity_id}`,
4005
+ className: "text-ui-fg-interactive",
4006
+ children: operation.entity_id
4007
+ }
4008
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: operation.entity_id }),
4009
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: operation.entity_type })
4148
4010
  ] }),
4149
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-ui-fg-subtle", children: selected.last_test_message }),
4150
- selected.last_test_at ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-xs text-ui-fg-muted", children: new Date(selected.last_test_at).toLocaleString("hu-HU") }) : null
4151
- ] }) : null,
4152
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t pt-5", children: [
4153
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
4011
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: operation.external_id || "—" }),
4012
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: operation.attempts }),
4013
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "max-w-md p-4", children: [
4014
+ operation.error_summary ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-error", children: operation.error_summary }) : null,
4015
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: formatDate$1(operation.updated_at) })
4016
+ ] }),
4017
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-w-52 flex-wrap gap-2", children: operation.status === "needs_reconciliation" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4154
4018
  /* @__PURE__ */ jsxRuntime.jsx(
4155
4019
  ui.Button,
4156
4020
  {
4157
- isLoading: saving,
4158
- disabled: saving || testing || !hasUnsavedChanges,
4159
- onClick: () => void save(),
4160
- children: "Mentés"
4021
+ size: "small",
4022
+ disabled: Boolean(actionLoading),
4023
+ isLoading: actionLoading === operation.id,
4024
+ onClick: () => void reconcile(operation, "confirmed_succeeded"),
4025
+ children: "Megtörtént"
4161
4026
  }
4162
4027
  ),
4163
4028
  /* @__PURE__ */ jsxRuntime.jsx(
4164
4029
  ui.Button,
4165
4030
  {
4031
+ size: "small",
4166
4032
  variant: "secondary",
4167
- isLoading: testing,
4168
- disabled: saving || testing || hasUnsavedChanges || !selected.configured || !selected.enabled,
4169
- onClick: () => void test(),
4170
- children: "Kapcsolat tesztelése"
4033
+ disabled: Boolean(actionLoading),
4034
+ onClick: () => void reconcile(operation, "confirmed_not_executed"),
4035
+ children: "Nem történt meg"
4171
4036
  }
4172
- ),
4173
- detailHref ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: detailHref, className: "ml-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Részletes nézet" }) }) : null
4174
- ] }),
4175
- hasUnsavedChanges ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-warning", children: "A kapcsolat-teszt mindig a mentett konfigurációt használja. Mentsd a módosításokat a teszt előtt." }) : !selected.enabled ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-muted", children: "Kapcsolat-teszt csak bekapcsolt integráción futtatható." }) : !selected.configured ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-muted", children: "Töltsd ki a kötelező mezőket és mentsd az integrációt a teszteléshez." }) : null
4176
- ] })
4177
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Nincs kiválasztott integráció." }) })
4037
+ )
4038
+ ] }) : operation.status === "failed" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "A normál rendelési művelet újraindítható." }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "—" }) }) })
4039
+ ] }, operation.id)) })
4040
+ ] }),
4041
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Műveletek betöltése…" }) }) : null,
4042
+ !loading && loadError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "m-6 rounded-lg border border-ui-border-error p-4", children: [
4043
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-error", children: "Az integrációs műveletek nem tölthetők be." }),
4044
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: loadError })
4045
+ ] }) : null,
4046
+ !loading && !loadError && !visible.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Nincs a szűrésnek megfelelő integrációs művelet." }) }) : null
4178
4047
  ] })
4179
4048
  ] });
4180
4049
  };
4181
- function Notice({ title, children }) {
4182
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border bg-ui-bg-subtle p-4", children: [
4183
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: title }),
4184
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children })
4185
- ] });
4186
- }
4187
- function FieldEditor({
4188
- field,
4050
+ function SummaryCard({
4051
+ label,
4189
4052
  value,
4190
- hasSecret,
4191
- clearPending,
4192
- onChange,
4193
- onClearSecret,
4194
- onRestoreSecret
4053
+ danger = false
4195
4054
  }) {
4196
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [
4197
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "font-medium", children: [
4198
- field.label,
4199
- field.required ? " *" : ""
4200
- ] }),
4201
- field.type === "boolean" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center gap-2", children: [
4202
- /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: Boolean(value), onCheckedChange: onChange }),
4203
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: value ? "Igen" : "Nem" })
4204
- ] }) : field.type === "select" ? /* @__PURE__ */ jsxRuntime.jsxs(
4205
- "select",
4206
- {
4207
- className: "h-8 rounded-md border bg-ui-bg-field px-2 text-sm",
4208
- value: String(value || ""),
4209
- onChange: (event) => onChange(event.target.value),
4210
- children: [
4211
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "—" }),
4212
- (field.options || []).map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, children: option.label }, option.value))
4213
- ]
4214
- }
4215
- ) : /* @__PURE__ */ jsxRuntime.jsx(
4216
- ui.Input,
4217
- {
4218
- type: field.type === "secret" ? "password" : field.type === "number" ? "number" : "text",
4219
- value: String(value || ""),
4220
- placeholder: field.type === "secret" && hasSecret && !clearPending ? "•••••••• (beállítva – hagyd üresen a megtartáshoz)" : field.help,
4221
- onChange: (event) => onChange(event.target.value)
4055
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4", children: [
4056
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: label }),
4057
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: danger ? "text-ui-fg-error text-2xl font-semibold" : "text-2xl font-semibold", children: value })
4058
+ ] });
4059
+ }
4060
+ function StatusText({ status }) {
4061
+ const labels = {
4062
+ pending: "Várakozik",
4063
+ processing: "Folyamatban",
4064
+ succeeded: "Sikeres",
4065
+ failed: "Sikertelen",
4066
+ needs_reconciliation: "Egyeztetést igényel"
4067
+ };
4068
+ const className = status === "failed" || status === "needs_reconciliation" ? "font-medium text-ui-fg-error" : status === "succeeded" ? "font-medium text-ui-fg-success" : "font-medium";
4069
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: labels[status] });
4070
+ }
4071
+ function providerLabel(value) {
4072
+ const labels = {
4073
+ stripe: "Stripe",
4074
+ simplepay: "SimplePay",
4075
+ billingo: "Billingo",
4076
+ foxpost: "FOXPOST",
4077
+ gls: "GLS",
4078
+ packeta: "Packeta",
4079
+ dpd: "DPD",
4080
+ mpl: "MPL"
4081
+ };
4082
+ return labels[value] || value;
4083
+ }
4084
+ function formatDate$1(value) {
4085
+ if (!value) return "—";
4086
+ const date = new Date(value);
4087
+ if (Number.isNaN(date.getTime())) return "—";
4088
+ return new Intl.DateTimeFormat("hu-HU", {
4089
+ dateStyle: "short",
4090
+ timeStyle: "short"
4091
+ }).format(date);
4092
+ }
4093
+ const config$2 = adminSdk.defineRouteConfig({
4094
+ label: "Integrációs műveletek",
4095
+ icon: Newspaper,
4096
+ rank: 37
4097
+ });
4098
+ const backendUrl$2 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
4099
+ const adminBase$1 = (__BASE__ || "/app").replace(/\/+$/, "");
4100
+ const apiUrl$2 = (path) => `${backendUrl$2}${path.startsWith("/") ? path : `/${path}`}`;
4101
+ const CATEGORY_LABELS = {
4102
+ infrastructure: "Infrastruktúra",
4103
+ security: "Biztonság",
4104
+ store: "Storefront alapok",
4105
+ commerce: "Kereskedelem",
4106
+ communication: "Kommunikáció",
4107
+ compliance: "Jogi megfelelés",
4108
+ marketing: "Mérés és SEO"
4109
+ };
4110
+ const ProductionReadinessPage = () => {
4111
+ const [report, setReport] = React.useState(null);
4112
+ const [loading, setLoading] = React.useState(true);
4113
+ const [error, setError] = React.useState(null);
4114
+ const load = React.useCallback(async () => {
4115
+ setLoading(true);
4116
+ setError(null);
4117
+ try {
4118
+ const response = await fetch(apiUrl$2("/admin/production-readiness"), {
4119
+ credentials: "include"
4120
+ });
4121
+ const payload = await response.json().catch(() => ({}));
4122
+ if (!response.ok) {
4123
+ throw new Error(payload.message || "Az élesítési ellenőrzés nem tölthető be.");
4222
4124
  }
4223
- ),
4224
- field.type === "secret" && hasSecret ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
4225
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: `text-xs ${clearPending ? "text-ui-fg-error" : "text-ui-fg-muted"}`, children: clearPending ? "A mentett titok a következő mentéskor törlődik." : "Titkos érték beállítva." }),
4226
- /* @__PURE__ */ jsxRuntime.jsx(
4227
- "button",
4228
- {
4229
- type: "button",
4230
- className: "shrink-0 text-xs font-medium text-ui-fg-interactive hover:underline",
4231
- onClick: clearPending ? onRestoreSecret : onClearSecret,
4232
- children: clearPending ? "Törlés visszavonása" : "Titok törlése"
4233
- }
4234
- )
4235
- ] }) : field.help ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-muted", children: field.help }) : null
4125
+ setReport(payload);
4126
+ } catch (cause) {
4127
+ setReport(null);
4128
+ setError(
4129
+ cause instanceof Error ? cause.message : "Az élesítési ellenőrzés nem tölthető be."
4130
+ );
4131
+ } finally {
4132
+ setLoading(false);
4133
+ }
4134
+ }, []);
4135
+ React.useEffect(() => {
4136
+ void load();
4137
+ }, [load]);
4138
+ const groups = React.useMemo(() => {
4139
+ const result = /* @__PURE__ */ new Map();
4140
+ for (const item of report?.checks || []) {
4141
+ result.set(item.category, [...result.get(item.category) || [], item]);
4142
+ }
4143
+ return Array.from(result.entries());
4144
+ }, [report]);
4145
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
4146
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4 border-b px-6 py-5", children: [
4147
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4148
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Production readiness" }),
4149
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-ui-fg-subtle", children: "Élesítés előtti runtime ellenőrzés. A felület státuszokat mutat, secret értéket nem ad vissza." })
4150
+ ] }),
4151
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", isLoading: loading, onClick: () => void load(), children: "Újraellenőrzés" })
4152
+ ] }),
4153
+ report ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4154
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 border-b p-6 sm:grid-cols-2 lg:grid-cols-4", children: [
4155
+ /* @__PURE__ */ jsxRuntime.jsx(Summary, { label: "Kész", value: report.score.ready, tone: "ready" }),
4156
+ /* @__PURE__ */ jsxRuntime.jsx(Summary, { label: "Figyelmeztetés", value: report.score.warning, tone: "warning" }),
4157
+ /* @__PURE__ */ jsxRuntime.jsx(Summary, { label: "Blokkoló", value: report.score.blocked, tone: "blocked" }),
4158
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4", children: [
4159
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Élesítési döntés" }),
4160
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: report.ready ? "mt-1 text-xl font-semibold text-ui-fg-success" : "mt-1 text-xl font-semibold text-ui-fg-error", children: report.ready ? "Élesíthető" : "Még nem élesíthető" }),
4161
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "mt-1 text-ui-fg-subtle", children: report.environment === "production" ? "Production szabályok" : "Nem-production szabályok" })
4162
+ ] })
4163
+ ] }),
4164
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b px-6 py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
4165
+ "Utolsó ellenőrzés: ",
4166
+ formatDate(report.generated_at),
4167
+ ". A sárga elemek nem blokkolják az élesítést, a piros elemek igen."
4168
+ ] }) }),
4169
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-6 p-6", children: groups.map(([category, checks]) => /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "overflow-hidden rounded-lg border", children: [
4170
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b bg-ui-bg-subtle px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: CATEGORY_LABELS[category] || category }) }),
4171
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: checks.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 p-4 md:grid-cols-[180px_140px_minmax(0,1fr)_auto] md:items-center", children: [
4172
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: item.label }),
4173
+ /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: item.status }),
4174
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: item.message }),
4175
+ item.action_href && item.action_label ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: normalizeAdminHref(item.action_href), children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: item.action_label }) }) : /* @__PURE__ */ jsxRuntime.jsx("span", {})
4176
+ ] }, item.key)) })
4177
+ ] }, category)) })
4178
+ ] }) : null,
4179
+ loading && !report ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Élesítési feltételek ellenőrzése…" }) }) : null,
4180
+ error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "m-6 rounded-lg border border-ui-border-error p-4", children: [
4181
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-error", children: "Az ellenőrzés nem futott le." }),
4182
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: error })
4183
+ ] }) : null
4236
4184
  ] });
4237
- }
4238
- function StatusBox({
4239
- label,
4240
- value,
4241
- state
4242
- }) {
4243
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-3", children: [
4244
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-muted", children: label }),
4245
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: connectionTextClass(state), children: value })
4185
+ };
4186
+ function StatusBadge({ status }) {
4187
+ const label = status === "ready" ? "Kész" : status === "warning" ? "Figyelmeztetés" : "Blokkoló";
4188
+ const className = status === "ready" ? "font-medium text-ui-fg-success" : status === "warning" ? "font-medium text-ui-fg-warning" : "font-medium text-ui-fg-error";
4189
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className, children: [
4190
+ status === "ready" ? "✅" : status === "warning" ? "🟡" : "🔴",
4191
+ " ",
4192
+ label
4246
4193
  ] });
4247
4194
  }
4248
- function detailPageForIntegration(key) {
4249
- if (key === "google-merchant") return `${adminBase$1}/marketing/google-merchant`;
4250
- if (key === "arukereso") return `${adminBase$1}/marketing/arukereso`;
4251
- return null;
4195
+ function Summary({ label, value, tone }) {
4196
+ const valueClass = tone === "ready" ? "text-ui-fg-success" : tone === "warning" ? "text-ui-fg-warning" : "text-ui-fg-error";
4197
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4", children: [
4198
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: label }),
4199
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `mt-1 text-3xl font-semibold ${valueClass}`, children: value })
4200
+ ] });
4252
4201
  }
4253
- function connectionPresentation(item) {
4254
- if (item.last_test_status === "ok") {
4255
- return { label: "Ellenőrizve", state: "success" };
4256
- }
4257
- if (item.last_test_status === "error") {
4258
- return { label: "Hiba", state: "error" };
4259
- }
4260
- if (item.last_test_at) {
4261
- return { label: "Részleges ellenőrzés", state: "warning" };
4262
- }
4263
- return { label: "Nem ellenőrzött", state: "neutral" };
4202
+ function normalizeAdminHref(path) {
4203
+ if (!path.startsWith("/app")) return path;
4204
+ return `${adminBase$1}${path.slice(4)}`;
4264
4205
  }
4265
- function connectionTextClass(state) {
4266
- if (state === "success") return "font-medium text-ui-fg-success";
4267
- if (state === "warning") return "font-medium text-ui-fg-warning";
4268
- if (state === "error") return "font-medium text-ui-fg-error";
4269
- return "font-medium";
4206
+ function formatDate(value) {
4207
+ const date = new Date(value);
4208
+ if (Number.isNaN(date.getTime())) return "—";
4209
+ return new Intl.DateTimeFormat("hu-HU", { dateStyle: "short", timeStyle: "medium" }).format(date);
4270
4210
  }
4271
- const config$1 = adminSdk.defineRouteConfig({ label: "Integrációk" });
4211
+ const config$1 = adminSdk.defineRouteConfig({
4212
+ label: "Production readiness",
4213
+ rank: 39
4214
+ });
4272
4215
  const backendUrl$1 = (__BACKEND_URL__ || "").replace(/\/+$/, "");
4273
4216
  const adminBase = (__BASE__ || "/app").replace(/\/+$/, "");
4274
- function apiUrl$1(path) {
4275
- return `${backendUrl$1}${path.startsWith("/") ? path : `/${path}`}`;
4276
- }
4277
- const IntegrationOperationsPage = () => {
4278
- const [operations, setOperations] = React.useState([]);
4279
- const [summary, setSummary] = React.useState({
4280
- failed: 0,
4281
- needsReconciliation: 0,
4282
- processing: 0
4283
- });
4284
- const [count, setCount] = React.useState(0);
4285
- const [status, setStatus] = React.useState("problems");
4286
- const [provider, setProvider] = React.useState("all");
4287
- const [query, setQuery] = React.useState("");
4288
- const [loading, setLoading] = React.useState(false);
4289
- const [actionLoading, setActionLoading] = React.useState(null);
4290
- const [loadError, setLoadError] = React.useState(null);
4217
+ const apiUrl$1 = (path) => `${backendUrl$1}${path.startsWith("/") ? path : `/${path}`}`;
4218
+ const CATEGORY_ORDER = [
4219
+ "payment",
4220
+ "invoicing",
4221
+ "shipping",
4222
+ "notification",
4223
+ "legal",
4224
+ "marketing"
4225
+ ];
4226
+ const CATEGORY_LABEL = {
4227
+ payment: "Fizetés",
4228
+ invoicing: "Számlázás",
4229
+ shipping: "Szállítás",
4230
+ notification: "Értesítés",
4231
+ legal: "Jogi integráció",
4232
+ marketing: "Marketing"
4233
+ };
4234
+ const IntegrationsPage = () => {
4235
+ const [items, setItems] = React.useState([]);
4236
+ const [selectedKey, setSelectedKey] = React.useState(null);
4237
+ const [draft, setDraft] = React.useState({});
4238
+ const [clearSecrets, setClearSecrets] = React.useState([]);
4239
+ const [enabled, setEnabled] = React.useState(false);
4240
+ const [loading, setLoading] = React.useState(true);
4241
+ const [saving, setSaving] = React.useState(false);
4242
+ const [testing, setTesting] = React.useState(false);
4243
+ const [restartNotice, setRestartNotice] = React.useState(false);
4244
+ const visibleItems = React.useMemo(
4245
+ () => items.filter((item) => CATEGORY_ORDER.includes(item.category)),
4246
+ [items]
4247
+ );
4248
+ const selected = React.useMemo(
4249
+ () => visibleItems.find((item) => item.key === selectedKey) || null,
4250
+ [visibleItems, selectedKey]
4251
+ );
4252
+ const hasUnsavedChanges = React.useMemo(() => {
4253
+ if (!selected) return false;
4254
+ if (enabled !== selected.enabled || clearSecrets.length > 0) return true;
4255
+ return selected.fields.some((field) => {
4256
+ const value = draft[field.name];
4257
+ if (field.type === "secret") {
4258
+ return Boolean(String(value || "").trim());
4259
+ }
4260
+ if (field.type === "boolean") {
4261
+ return Boolean(value) !== (selected.values[field.name] === "true");
4262
+ }
4263
+ return String(value || "").trim() !== String(selected.values[field.name] || "").trim();
4264
+ });
4265
+ }, [selected, enabled, draft, clearSecrets]);
4291
4266
  const load = React.useCallback(async () => {
4292
4267
  setLoading(true);
4293
- setLoadError(null);
4294
4268
  try {
4295
- const params = new URLSearchParams({
4296
- limit: "100",
4297
- status
4269
+ const response = await fetch(apiUrl$1("/admin/integrations"), {
4270
+ credentials: "include"
4298
4271
  });
4299
- if (provider !== "all") params.set("provider", provider);
4300
- const response = await fetch(
4301
- apiUrl$1(`/admin/integration-operations?${params.toString()}`),
4302
- { credentials: "include" }
4303
- );
4304
- const payload = await response.json().catch(() => ({}));
4272
+ const data = await response.json().catch(() => ({}));
4305
4273
  if (!response.ok) {
4306
- throw new Error(
4307
- payload.message || "Az integrációs műveletek nem tölthetők be."
4308
- );
4274
+ throw new Error(data.message || "Az integrációk nem tölthetők be.");
4309
4275
  }
4310
- setOperations(payload.operations || []);
4311
- setCount(payload.count || 0);
4312
- setSummary(
4313
- payload.summary || {
4314
- failed: 0,
4315
- needsReconciliation: 0,
4316
- processing: 0
4317
- }
4276
+ const integrations = data.integrations || [];
4277
+ setItems(integrations);
4278
+ const eligible = integrations.filter(
4279
+ (item) => CATEGORY_ORDER.includes(item.category)
4280
+ );
4281
+ setSelectedKey(
4282
+ (current) => current && eligible.some((item) => item.key === current) ? current : eligible[0]?.key || null
4318
4283
  );
4319
4284
  } catch (error) {
4320
- setOperations([]);
4321
- setLoadError(
4322
- error instanceof Error ? error.message : "Az integrációs műveletek nem tölthetők be."
4285
+ ui.toast.error(
4286
+ error instanceof Error ? error.message : "Az integrációk nem tölthetők be."
4323
4287
  );
4324
4288
  } finally {
4325
4289
  setLoading(false);
4326
4290
  }
4327
- }, [provider, status]);
4291
+ }, []);
4328
4292
  React.useEffect(() => {
4329
4293
  void load();
4330
4294
  }, [load]);
4331
- const visible = React.useMemo(() => {
4332
- const value = query.trim().toLowerCase();
4333
- if (!value) return operations;
4334
- return operations.filter(
4335
- (operation) => [
4336
- operation.provider,
4337
- operation.operation_type,
4338
- operation.entity_type,
4339
- operation.entity_id,
4340
- operation.external_id,
4341
- operation.error_summary
4342
- ].some(
4343
- (item) => String(item || "").toLowerCase().includes(value)
4344
- )
4345
- );
4346
- }, [operations, query]);
4347
- const reconcile = async (operation, decision) => {
4348
- const confirmed = confirm(
4349
- decision === "confirmed_succeeded" ? "Megerősíted, hogy a külső szolgáltatónál ez a művelet valóban sikeresen megtörtént?" : "Megerősíted, hogy a külső szolgáltatónál ez a művelet biztosan NEM történt meg? Ez engedélyezi a normál művelet későbbi újrapróbálását."
4350
- );
4351
- if (!confirmed) return;
4352
- let externalId = operation.external_id || void 0;
4353
- if (decision === "confirmed_succeeded" && !externalId) {
4354
- externalId = prompt(
4355
- "Add meg a külső szolgáltató azonosítóját (pl. Billingo document ID vagy shipment ID):"
4356
- )?.trim() || void 0;
4357
- if (!externalId) {
4358
- ui.toast.error("A sikeres lezáráshoz külső azonosító szükséges.");
4359
- return;
4295
+ React.useEffect(() => {
4296
+ if (!selected) return;
4297
+ const next = {};
4298
+ for (const field of selected.fields) {
4299
+ if (field.type === "secret") next[field.name] = "";
4300
+ else if (field.type === "boolean") {
4301
+ next[field.name] = selected.values[field.name] === "true";
4302
+ } else {
4303
+ next[field.name] = selected.values[field.name] || "";
4304
+ }
4305
+ }
4306
+ setDraft(next);
4307
+ setClearSecrets([]);
4308
+ setEnabled(selected.enabled);
4309
+ setRestartNotice(false);
4310
+ }, [selected?.key, selected?.values, selected?.enabled]);
4311
+ const save = async () => {
4312
+ if (!selected) return;
4313
+ setSaving(true);
4314
+ try {
4315
+ const values = {};
4316
+ for (const field of selected.fields) {
4317
+ const value = draft[field.name];
4318
+ if (field.type === "secret" && !String(value || "").trim()) continue;
4319
+ values[field.name] = value;
4320
+ }
4321
+ const response = await fetch(apiUrl$1(`/admin/integrations/${selected.key}`), {
4322
+ method: "POST",
4323
+ credentials: "include",
4324
+ headers: { "Content-Type": "application/json" },
4325
+ body: JSON.stringify({
4326
+ enabled,
4327
+ values,
4328
+ clear_secrets: clearSecrets
4329
+ })
4330
+ });
4331
+ const data = await response.json().catch(() => ({}));
4332
+ if (!response.ok) {
4333
+ throw new Error(data.message || "A mentés nem sikerült.");
4360
4334
  }
4335
+ if (data.integration) replaceIntegration(data.integration);
4336
+ setClearSecrets([]);
4337
+ setRestartNotice(Boolean(data.integration?.restart_required));
4338
+ ui.toast.success(data.message || "Beállítások mentve.");
4339
+ } catch (error) {
4340
+ ui.toast.error(error instanceof Error ? error.message : "A mentés nem sikerült.");
4341
+ } finally {
4342
+ setSaving(false);
4361
4343
  }
4362
- const note = prompt(
4363
- "Opcionális megjegyzés az egyeztetéshez:",
4364
- ""
4365
- )?.trim();
4366
- setActionLoading(operation.id);
4344
+ };
4345
+ const test = async () => {
4346
+ if (!selected || hasUnsavedChanges) return;
4347
+ setTesting(true);
4367
4348
  try {
4368
- const response = await fetch(
4369
- apiUrl$1(`/admin/integration-operations/${operation.id}/reconcile`),
4370
- {
4371
- method: "POST",
4372
- credentials: "include",
4373
- headers: { "Content-Type": "application/json" },
4374
- body: JSON.stringify({ decision, externalId, note })
4375
- }
4376
- );
4377
- const payload = await response.json().catch(() => ({}));
4378
- if (!response.ok) {
4379
- throw new Error(
4380
- payload.message || "Az egyeztetés lezárása nem sikerült."
4381
- );
4349
+ const response = await fetch(apiUrl$1(`/admin/integrations/${selected.key}/test`), {
4350
+ method: "POST",
4351
+ credentials: "include"
4352
+ });
4353
+ const data = await response.json().catch(() => ({}));
4354
+ if (data.integration) replaceIntegration(data.integration);
4355
+ if (!response.ok || !data.ok) {
4356
+ throw new Error(data.message || "A kapcsolat tesztelése nem sikerült.");
4357
+ }
4358
+ if (data.verified) {
4359
+ ui.toast.success(data.message || "Kapcsolat ellenőrizve.");
4360
+ } else {
4361
+ ui.toast.success(data.message || "Részleges ellenőrzés sikeres.");
4382
4362
  }
4383
- ui.toast.success(
4384
- decision === "confirmed_succeeded" ? "A művelet sikeresként lezárva." : "A művelet újrapróbálhatóvá téve."
4385
- );
4386
- await load();
4387
4363
  } catch (error) {
4388
4364
  ui.toast.error(
4389
- error instanceof Error ? error.message : "Az egyeztetés lezárása nem sikerült."
4365
+ error instanceof Error ? error.message : "A kapcsolat tesztelése nem sikerült."
4390
4366
  );
4367
+ void load();
4391
4368
  } finally {
4392
- setActionLoading(null);
4369
+ setTesting(false);
4393
4370
  }
4394
4371
  };
4395
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
4396
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 border-b px-6 py-4", children: [
4397
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4398
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Integrációs műveletek" }),
4399
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Külső API műveletek, hibák és kézi reconciliation kezelése." })
4400
- ] }),
4401
- /* @__PURE__ */ jsxRuntime.jsx(
4402
- ui.Button,
4403
- {
4404
- variant: "secondary",
4405
- isLoading: loading,
4406
- onClick: () => void load(),
4407
- children: "Frissítés"
4408
- }
4372
+ const replaceIntegration = (integration) => {
4373
+ setItems(
4374
+ (current) => current.map(
4375
+ (item) => item.key === integration.key ? integration : item
4409
4376
  )
4377
+ );
4378
+ };
4379
+ const detailHref = selected ? detailPageForIntegration(selected.key) : null;
4380
+ const connection = selected ? connectionPresentation(selected) : null;
4381
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
4382
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b px-6 py-5", children: [
4383
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Integrációk" }),
4384
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-ui-fg-subtle", children: "Külső szolgáltatások és provider kapcsolatok központi beállítása. A secret értékek nem kerülnek vissza a böngészőbe, a provider-hozzárendelés pedig továbbra is a HSZRDM Commerce Admin natív felületein történik." })
4410
4385
  ] }),
4411
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 border-b px-6 py-4 md:grid-cols-3", children: [
4412
- /* @__PURE__ */ jsxRuntime.jsx(
4413
- SummaryCard,
4414
- {
4415
- label: "Egyeztetést igényel",
4416
- value: summary.needsReconciliation,
4417
- danger: summary.needsReconciliation > 0
4418
- }
4419
- ),
4420
- /* @__PURE__ */ jsxRuntime.jsx(
4421
- SummaryCard,
4422
- {
4423
- label: "Sikertelen",
4424
- value: summary.failed,
4425
- danger: summary.failed > 0
4426
- }
4427
- ),
4428
- /* @__PURE__ */ jsxRuntime.jsx(SummaryCard, { label: "Folyamatban", value: summary.processing })
4429
- ] }),
4430
- summary.needsReconciliation > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-ui-border-warning bg-ui-bg-base px-6 py-4", children: [
4431
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "font-medium text-ui-fg-warning", children: [
4432
- summary.needsReconciliation,
4433
- " műveletnél nem dönthető el automatikusan, hogy a külső szolgáltató végrehajtotta-e a kérést."
4434
- ] }),
4435
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Ellenőrizd a szolgáltatónál, majd csak ezután jelöld sikeresnek vagy újrapróbálhatónak. Így elkerülhető a duplikált számla, csomag vagy fizetés." })
4436
- ] }) : null,
4437
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-end gap-3 border-b px-6 py-4", children: [
4438
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
4439
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Státusz" }),
4440
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: status, onValueChange: setStatus, children: [
4441
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { className: "w-52", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, {}) }),
4442
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
4443
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "problems", children: "Hibák + egyeztetés" }),
4444
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "needs_reconciliation", children: "Egyeztetést igényel" }),
4445
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "failed", children: "Sikertelen" }),
4446
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "processing", children: "Folyamatban" }),
4447
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "pending", children: "Várakozik" }),
4448
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "succeeded", children: "Sikeres" }),
4449
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "all", children: "Összes" })
4450
- ] })
4451
- ] })
4452
- ] }),
4453
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
4454
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Provider" }),
4455
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: provider, onValueChange: setProvider, children: [
4456
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { className: "w-44", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, {}) }),
4457
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
4458
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "all", children: "Összes" }),
4459
- [
4460
- "stripe",
4461
- "simplepay",
4462
- "billingo",
4463
- "foxpost",
4464
- "gls",
4465
- "packeta",
4466
- "dpd",
4467
- "mpl"
4468
- ].map((item) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: item, children: providerLabel(item) }, item))
4469
- ] })
4470
- ] })
4471
- ] }),
4472
- /* @__PURE__ */ jsxRuntime.jsx(
4473
- ui.Input,
4474
- {
4475
- className: "max-w-sm",
4476
- value: query,
4477
- onChange: (event) => setQuery(event.target.value),
4478
- placeholder: "Keresés művelet, rendelés, külső ID vagy hiba alapján"
4479
- }
4480
- ),
4481
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
4482
- count,
4483
- " találat"
4484
- ] })
4485
- ] }),
4486
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-x-auto", children: [
4487
- /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-left text-sm", children: [
4488
- /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "border-b bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
4489
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Státusz" }),
4490
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Provider / művelet" }),
4491
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Entitás" }),
4492
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Külső ID" }),
4493
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Próbálkozás" }),
4494
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Hiba / idő" }),
4495
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "p-4", children: "Műveletek" })
4496
- ] }) }),
4497
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: visible.map((operation) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b align-top", children: [
4498
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(StatusText, { status: operation.status }) }),
4499
- /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
4500
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: providerLabel(operation.provider) }),
4501
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: operation.operation_type })
4502
- ] }),
4503
- /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "p-4", children: [
4504
- operation.entity_type === "order" ? /* @__PURE__ */ jsxRuntime.jsx(
4505
- "a",
4386
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Betöltés…" }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid lg:grid-cols-[320px_minmax(0,1fr)]", children: [
4387
+ /* @__PURE__ */ jsxRuntime.jsx("aside", { className: "border-r p-4", children: CATEGORY_ORDER.map((category) => {
4388
+ const categoryItems = visibleItems.filter(
4389
+ (item) => item.category === category
4390
+ );
4391
+ if (!categoryItems.length) return null;
4392
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 last:mb-0", children: [
4393
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-2 px-1 text-xs font-medium uppercase tracking-wide text-ui-fg-muted", children: CATEGORY_LABEL[category] }),
4394
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: categoryItems.map((item) => {
4395
+ const itemConnection = connectionPresentation(item);
4396
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4397
+ "button",
4506
4398
  {
4507
- href: `${adminBase}/orders/${operation.entity_id}`,
4508
- className: "text-ui-fg-interactive",
4509
- children: operation.entity_id
4510
- }
4511
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: operation.entity_id }),
4512
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: operation.entity_type })
4399
+ type: "button",
4400
+ onClick: () => setSelectedKey(item.key),
4401
+ className: `rounded-lg border p-3 text-left transition-colors hover:bg-ui-bg-subtle ${selectedKey === item.key ? "bg-ui-bg-subtle" : "bg-ui-bg-base"}`,
4402
+ children: [
4403
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
4404
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: item.label }),
4405
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: item.enabled ? "text-xs font-medium text-ui-fg-success" : "text-xs text-ui-fg-muted", children: item.enabled ? "Aktív" : "Kikapcsolva" })
4406
+ ] }),
4407
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 text-xs text-ui-fg-subtle", children: [
4408
+ item.configured ? "Konfigurálva" : "Beállítás szükséges",
4409
+ " · ",
4410
+ itemConnection.label
4411
+ ] })
4412
+ ]
4413
+ },
4414
+ item.key
4415
+ );
4416
+ }) })
4417
+ ] }, category);
4418
+ }) }),
4419
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: "min-w-0 p-6", children: selected && connection ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-w-4xl flex-col gap-6", children: [
4420
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [
4421
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
4422
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: selected.label }),
4423
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-ui-fg-subtle", children: selected.description })
4513
4424
  ] }),
4514
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: operation.external_id || "—" }),
4515
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: operation.attempts }),
4516
- /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "max-w-md p-4", children: [
4517
- operation.error_summary ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-error", children: operation.error_summary }) : null,
4518
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: formatDate(operation.updated_at) })
4425
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end gap-1", children: [
4426
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2", children: [
4427
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: enabled, onCheckedChange: setEnabled }),
4428
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: enabled ? "Bekapcsolva" : "Kikapcsolva" })
4429
+ ] }),
4430
+ hasUnsavedChanges ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-warning", children: "Nem mentett módosítások" }) : null
4431
+ ] })
4432
+ ] }),
4433
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-4", children: [
4434
+ /* @__PURE__ */ jsxRuntime.jsx(StatusBox, { label: "Telepítve", value: selected.installed ? "Igen" : "Nem", state: selected.installed ? "success" : "neutral" }),
4435
+ /* @__PURE__ */ jsxRuntime.jsx(StatusBox, { label: "Konfigurálva", value: selected.configured ? "Igen" : "Nem", state: selected.configured ? "success" : "neutral" }),
4436
+ /* @__PURE__ */ jsxRuntime.jsx(StatusBox, { label: "Aktív", value: selected.enabled ? "Igen" : "Nem", state: selected.enabled ? "success" : "neutral" }),
4437
+ /* @__PURE__ */ jsxRuntime.jsx(StatusBox, { label: "Kapcsolat", value: connection.label, state: connection.state })
4438
+ ] }),
4439
+ selected.category === "shipping" ? /* @__PURE__ */ jsxRuntime.jsx(Notice, { title: "HSZRDM Commerce provider hozzárendelés", children: "Mentés és backend restart után a providert a HSZRDM Commerce Admin Settings → Locations & Shipping alatt rendeld locationhöz." }) : null,
4440
+ selected.category === "legal" ? /* @__PURE__ */ jsxRuntime.jsx(Notice, { title: "Fogyasztó Barát EMBED", children: "Ez csak az opcionális Fogyasztó Barát embed konfigurációja. A PDF-ek és checkout elfogadások a főoldali Jogi dokumentumok menüpontban kezelhetők." }) : null,
4441
+ selected.category === "marketing" ? /* @__PURE__ */ jsxRuntime.jsx(Notice, { title: "Marketing feed", children: "Itt az integráció alapbeállításait módosítod. A feed előnézet, diagnosztika és publikus URL a főoldali Marketing menüpontban található." }) : null,
4442
+ restartNotice ? /* @__PURE__ */ jsxRuntime.jsx(Notice, { title: "Újraindítás szükséges", children: "A beállítások elmentve. Indítsd újra a HSZRDM Commerce backendet az új provider-konfiguráció betöltéséhez." }) : null,
4443
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4444
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Beállítások" }),
4445
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 grid gap-4 sm:grid-cols-2", children: selected.fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
4446
+ FieldEditor,
4447
+ {
4448
+ field,
4449
+ value: draft[field.name],
4450
+ hasSecret: Boolean(selected.secrets[field.name]),
4451
+ clearPending: clearSecrets.includes(field.name),
4452
+ onChange: (value) => {
4453
+ setDraft((current) => ({ ...current, [field.name]: value }));
4454
+ if (field.type === "secret" && String(value || "").trim()) {
4455
+ setClearSecrets(
4456
+ (current) => current.filter((name) => name !== field.name)
4457
+ );
4458
+ }
4459
+ },
4460
+ onClearSecret: () => {
4461
+ setDraft((current) => ({ ...current, [field.name]: "" }));
4462
+ setClearSecrets(
4463
+ (current) => current.includes(field.name) ? current : [...current, field.name]
4464
+ );
4465
+ },
4466
+ onRestoreSecret: () => setClearSecrets(
4467
+ (current) => current.filter((name) => name !== field.name)
4468
+ )
4469
+ },
4470
+ field.name
4471
+ )) })
4472
+ ] }),
4473
+ selected.last_test_message ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4", children: [
4474
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
4475
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: "Utolsó kapcsolat-teszt" }),
4476
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: connectionTextClass(connection.state), children: connection.label })
4519
4477
  ] }),
4520
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-w-52 flex-wrap gap-2", children: operation.status === "needs_reconciliation" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4478
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-ui-fg-subtle", children: selected.last_test_message }),
4479
+ selected.last_test_at ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-xs text-ui-fg-muted", children: new Date(selected.last_test_at).toLocaleString("hu-HU") }) : null
4480
+ ] }) : null,
4481
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t pt-5", children: [
4482
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
4521
4483
  /* @__PURE__ */ jsxRuntime.jsx(
4522
4484
  ui.Button,
4523
4485
  {
4524
- size: "small",
4525
- disabled: Boolean(actionLoading),
4526
- isLoading: actionLoading === operation.id,
4527
- onClick: () => void reconcile(operation, "confirmed_succeeded"),
4528
- children: "Megtörtént"
4486
+ isLoading: saving,
4487
+ disabled: saving || testing || !hasUnsavedChanges,
4488
+ onClick: () => void save(),
4489
+ children: "Mentés"
4529
4490
  }
4530
4491
  ),
4531
4492
  /* @__PURE__ */ jsxRuntime.jsx(
4532
4493
  ui.Button,
4533
4494
  {
4534
- size: "small",
4535
4495
  variant: "secondary",
4536
- disabled: Boolean(actionLoading),
4537
- onClick: () => void reconcile(operation, "confirmed_not_executed"),
4538
- children: "Nem történt meg"
4496
+ isLoading: testing,
4497
+ disabled: saving || testing || hasUnsavedChanges || !selected.configured || !selected.enabled,
4498
+ onClick: () => void test(),
4499
+ children: "Kapcsolat tesztelése"
4539
4500
  }
4540
- )
4541
- ] }) : operation.status === "failed" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "A normál rendelési művelet újraindítható." }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "—" }) }) })
4542
- ] }, operation.id)) })
4543
- ] }),
4544
- loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Műveletek betöltése…" }) }) : null,
4545
- !loading && loadError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "m-6 rounded-lg border border-ui-border-error p-4", children: [
4546
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-error", children: "Az integrációs műveletek nem tölthetők be." }),
4547
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: loadError })
4548
- ] }) : null,
4549
- !loading && !loadError && !visible.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Nincs a szűrésnek megfelelő integrációs művelet." }) }) : null
4501
+ ),
4502
+ detailHref ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: detailHref, className: "ml-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Részletes nézet" }) }) : null
4503
+ ] }),
4504
+ hasUnsavedChanges ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-warning", children: "A kapcsolat-teszt mindig a mentett konfigurációt használja. Mentsd a módosításokat a teszt előtt." }) : !selected.enabled ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-muted", children: "Kapcsolat-teszt csak bekapcsolt integráción futtatható." }) : !selected.configured ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-xs text-ui-fg-muted", children: "Töltsd ki a kötelező mezőket és mentsd az integrációt a teszteléshez." }) : null
4505
+ ] })
4506
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Nincs kiválasztott integráció." }) })
4550
4507
  ] })
4551
4508
  ] });
4552
4509
  };
4553
- function SummaryCard({
4510
+ function Notice({ title, children }) {
4511
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border bg-ui-bg-subtle p-4", children: [
4512
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: title }),
4513
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children })
4514
+ ] });
4515
+ }
4516
+ function FieldEditor({
4517
+ field,
4518
+ value,
4519
+ hasSecret,
4520
+ clearPending,
4521
+ onChange,
4522
+ onClearSecret,
4523
+ onRestoreSecret
4524
+ }) {
4525
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [
4526
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "font-medium", children: [
4527
+ field.label,
4528
+ field.required ? " *" : ""
4529
+ ] }),
4530
+ field.type === "boolean" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center gap-2", children: [
4531
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: Boolean(value), onCheckedChange: onChange }),
4532
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: value ? "Igen" : "Nem" })
4533
+ ] }) : field.type === "select" ? /* @__PURE__ */ jsxRuntime.jsxs(
4534
+ "select",
4535
+ {
4536
+ className: "h-8 rounded-md border bg-ui-bg-field px-2 text-sm",
4537
+ value: String(value || ""),
4538
+ onChange: (event) => onChange(event.target.value),
4539
+ children: [
4540
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "—" }),
4541
+ (field.options || []).map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, children: option.label }, option.value))
4542
+ ]
4543
+ }
4544
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
4545
+ ui.Input,
4546
+ {
4547
+ type: field.type === "secret" ? "password" : field.type === "number" ? "number" : "text",
4548
+ value: String(value || ""),
4549
+ placeholder: field.type === "secret" && hasSecret && !clearPending ? "•••••••• (beállítva – hagyd üresen a megtartáshoz)" : field.help,
4550
+ onChange: (event) => onChange(event.target.value)
4551
+ }
4552
+ ),
4553
+ field.type === "secret" && hasSecret ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
4554
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: `text-xs ${clearPending ? "text-ui-fg-error" : "text-ui-fg-muted"}`, children: clearPending ? "A mentett titok a következő mentéskor törlődik." : "Titkos érték beállítva." }),
4555
+ /* @__PURE__ */ jsxRuntime.jsx(
4556
+ "button",
4557
+ {
4558
+ type: "button",
4559
+ className: "shrink-0 text-xs font-medium text-ui-fg-interactive hover:underline",
4560
+ onClick: clearPending ? onRestoreSecret : onClearSecret,
4561
+ children: clearPending ? "Törlés visszavonása" : "Titok törlése"
4562
+ }
4563
+ )
4564
+ ] }) : field.help ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-muted", children: field.help }) : null
4565
+ ] });
4566
+ }
4567
+ function StatusBox({
4554
4568
  label,
4555
4569
  value,
4556
- danger = false
4570
+ state
4557
4571
  }) {
4558
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4", children: [
4559
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: label }),
4560
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: danger ? "text-ui-fg-error text-2xl font-semibold" : "text-2xl font-semibold", children: value })
4572
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-3", children: [
4573
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-muted", children: label }),
4574
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: connectionTextClass(state), children: value })
4561
4575
  ] });
4562
4576
  }
4563
- function StatusText({ status }) {
4564
- const labels = {
4565
- pending: "Várakozik",
4566
- processing: "Folyamatban",
4567
- succeeded: "Sikeres",
4568
- failed: "Sikertelen",
4569
- needs_reconciliation: "Egyeztetést igényel"
4570
- };
4571
- const className = status === "failed" || status === "needs_reconciliation" ? "font-medium text-ui-fg-error" : status === "succeeded" ? "font-medium text-ui-fg-success" : "font-medium";
4572
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: labels[status] });
4577
+ function detailPageForIntegration(key) {
4578
+ if (key === "google-merchant") return `${adminBase}/marketing/google-merchant`;
4579
+ if (key === "arukereso") return `${adminBase}/marketing/arukereso`;
4580
+ return null;
4573
4581
  }
4574
- function providerLabel(value) {
4575
- const labels = {
4576
- stripe: "Stripe",
4577
- simplepay: "SimplePay",
4578
- billingo: "Billingo",
4579
- foxpost: "FOXPOST",
4580
- gls: "GLS",
4581
- packeta: "Packeta",
4582
- dpd: "DPD",
4583
- mpl: "MPL"
4584
- };
4585
- return labels[value] || value;
4582
+ function connectionPresentation(item) {
4583
+ if (item.last_test_status === "ok") {
4584
+ return { label: "Ellenőrizve", state: "success" };
4585
+ }
4586
+ if (item.last_test_status === "error") {
4587
+ return { label: "Hiba", state: "error" };
4588
+ }
4589
+ if (item.last_test_at) {
4590
+ return { label: "Részleges ellenőrzés", state: "warning" };
4591
+ }
4592
+ return { label: "Nem ellenőrzött", state: "neutral" };
4586
4593
  }
4587
- function formatDate(value) {
4588
- if (!value) return "—";
4589
- const date = new Date(value);
4590
- if (Number.isNaN(date.getTime())) return "—";
4591
- return new Intl.DateTimeFormat("hu-HU", {
4592
- dateStyle: "short",
4593
- timeStyle: "short"
4594
- }).format(date);
4594
+ function connectionTextClass(state) {
4595
+ if (state === "success") return "font-medium text-ui-fg-success";
4596
+ if (state === "warning") return "font-medium text-ui-fg-warning";
4597
+ if (state === "error") return "font-medium text-ui-fg-error";
4598
+ return "font-medium";
4595
4599
  }
4596
- const config = adminSdk.defineRouteConfig({
4597
- label: "Integrációs műveletek",
4598
- icon: Newspaper,
4599
- rank: 37
4600
- });
4600
+ const config = adminSdk.defineRouteConfig({ label: "Integrációk" });
4601
4601
  const EMPTY = {
4602
4602
  sourcePath: "",
4603
4603
  targetPath: "",
@@ -4934,8 +4934,8 @@ const routeModule = {
4934
4934
  handle: { label: config$9.label, translationNs: config$9.translationNs }
4935
4935
  },
4936
4936
  {
4937
- Component: ArukeresoPage,
4938
- path: "/marketing/arukereso",
4937
+ Component: BillingPage,
4938
+ path: "/billing",
4939
4939
  handle: { label: config$8.label, translationNs: config$8.translationNs }
4940
4940
  },
4941
4941
  {
@@ -4944,42 +4944,42 @@ const routeModule = {
4944
4944
  handle: { label: config$7.label, translationNs: config$7.translationNs }
4945
4945
  },
4946
4946
  {
4947
- Component: BillingPage,
4948
- path: "/billing",
4947
+ Component: ArukeresoPage,
4948
+ path: "/marketing/arukereso",
4949
4949
  handle: { label: config$6.label, translationNs: config$6.translationNs }
4950
4950
  },
4951
4951
  {
4952
4952
  Component: SeoAdminPage,
4953
4953
  path: "/marketing/seo"
4954
4954
  },
4955
- {
4956
- Component: GoogleMerchantPage,
4957
- path: "/marketing/google-merchant",
4958
- handle: { label: config$5.label, translationNs: config$5.translationNs }
4959
- },
4960
4955
  {
4961
4956
  Component: CoreAuditPage,
4962
4957
  path: "/system-monitoring/audit",
4963
- handle: { label: config$4.label, translationNs: config$4.translationNs }
4958
+ handle: { label: config$5.label, translationNs: config$5.translationNs }
4964
4959
  },
4965
4960
  {
4966
4961
  Component: IntegrationHealthPage,
4967
4962
  path: "/system-monitoring/health",
4963
+ handle: { label: config$4.label, translationNs: config$4.translationNs }
4964
+ },
4965
+ {
4966
+ Component: GoogleMerchantPage,
4967
+ path: "/marketing/google-merchant",
4968
4968
  handle: { label: config$3.label, translationNs: config$3.translationNs }
4969
4969
  },
4970
4970
  {
4971
- Component: ProductionReadinessPage,
4972
- path: "/system-monitoring/readiness",
4971
+ Component: IntegrationOperationsPage,
4972
+ path: "/system-monitoring/operations",
4973
4973
  handle: { label: config$2.label, translationNs: config$2.translationNs }
4974
4974
  },
4975
4975
  {
4976
- Component: IntegrationsPage,
4977
- path: "/settings/integrations",
4976
+ Component: ProductionReadinessPage,
4977
+ path: "/system-monitoring/readiness",
4978
4978
  handle: { label: config$1.label, translationNs: config$1.translationNs }
4979
4979
  },
4980
4980
  {
4981
- Component: IntegrationOperationsPage,
4982
- path: "/system-monitoring/operations",
4981
+ Component: IntegrationsPage,
4982
+ path: "/settings/integrations",
4983
4983
  handle: { label: config.label, translationNs: config.translationNs }
4984
4984
  },
4985
4985
  {
@@ -4999,8 +4999,8 @@ const menuItemModule = {
4999
4999
  translationNs: void 0
5000
5000
  },
5001
5001
  {
5002
- label: config$6.label,
5003
- icon: config$6.icon,
5002
+ label: config$8.label,
5003
+ icon: config$8.icon,
5004
5004
  path: "/billing",
5005
5005
  nested: void 0,
5006
5006
  rank: 36,
@@ -5022,6 +5022,14 @@ const menuItemModule = {
5022
5022
  rank: void 0,
5023
5023
  translationNs: void 0
5024
5024
  },
5025
+ {
5026
+ label: config$a.label,
5027
+ icon: config$a.icon,
5028
+ path: "/shipping",
5029
+ nested: void 0,
5030
+ rank: 35,
5031
+ translationNs: void 0
5032
+ },
5025
5033
  {
5026
5034
  label: config$9.label,
5027
5035
  icon: void 0,
@@ -5031,23 +5039,23 @@ const menuItemModule = {
5031
5039
  translationNs: void 0
5032
5040
  },
5033
5041
  {
5034
- label: config$a.label,
5035
- icon: config$a.icon,
5036
- path: "/shipping",
5042
+ label: config.label,
5043
+ icon: void 0,
5044
+ path: "/settings/integrations",
5037
5045
  nested: void 0,
5038
- rank: 35,
5046
+ rank: void 0,
5039
5047
  translationNs: void 0
5040
5048
  },
5041
5049
  {
5042
- label: config$1.label,
5050
+ label: config$3.label,
5043
5051
  icon: void 0,
5044
- path: "/settings/integrations",
5052
+ path: "/marketing/google-merchant",
5045
5053
  nested: void 0,
5046
- rank: void 0,
5054
+ rank: 41,
5047
5055
  translationNs: void 0
5048
5056
  },
5049
5057
  {
5050
- label: config$8.label,
5058
+ label: config$6.label,
5051
5059
  icon: void 0,
5052
5060
  path: "/marketing/arukereso",
5053
5061
  nested: void 0,
@@ -5056,43 +5064,35 @@ const menuItemModule = {
5056
5064
  },
5057
5065
  {
5058
5066
  label: config$5.label,
5059
- icon: void 0,
5060
- path: "/marketing/google-merchant",
5067
+ icon: config$5.icon,
5068
+ path: "/system-monitoring/audit",
5061
5069
  nested: void 0,
5062
- rank: 41,
5070
+ rank: 39,
5063
5071
  translationNs: void 0
5064
5072
  },
5065
5073
  {
5066
5074
  label: config$4.label,
5067
5075
  icon: config$4.icon,
5068
- path: "/system-monitoring/audit",
5076
+ path: "/system-monitoring/health",
5069
5077
  nested: void 0,
5070
- rank: 39,
5078
+ rank: 38,
5071
5079
  translationNs: void 0
5072
5080
  },
5073
5081
  {
5074
- label: config.label,
5075
- icon: config.icon,
5082
+ label: config$2.label,
5083
+ icon: config$2.icon,
5076
5084
  path: "/system-monitoring/operations",
5077
5085
  nested: void 0,
5078
5086
  rank: 37,
5079
5087
  translationNs: void 0
5080
5088
  },
5081
5089
  {
5082
- label: config$2.label,
5090
+ label: config$1.label,
5083
5091
  icon: void 0,
5084
5092
  path: "/system-monitoring/readiness",
5085
5093
  nested: void 0,
5086
5094
  rank: 39,
5087
5095
  translationNs: void 0
5088
- },
5089
- {
5090
- label: config$3.label,
5091
- icon: config$3.icon,
5092
- path: "/system-monitoring/health",
5093
- nested: void 0,
5094
- rank: 38,
5095
- translationNs: void 0
5096
5096
  }
5097
5097
  ]
5098
5098
  };