@rozaqi02/reusable-dashboard 1.1.3 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1861,269 +1861,660 @@ import React18 from "react";
1861
1861
  import PropTypes18 from "prop-types";
1862
1862
 
1863
1863
  // src/presentation/SetupWizard.jsx
1864
- import React17, { useState as useState6 } from "react";
1864
+ import React17, { useState as useState6, useEffect as useEffect3, useCallback as useCallback5 } from "react";
1865
1865
  import PropTypes17 from "prop-types";
1866
- function SetupWizard({ issues = [], onDismiss, supabase }) {
1867
- const [step, setStep] = useState6(0);
1868
- const [tables, setTables] = useState6(null);
1869
- const [loadingTables, setLoadingTables] = useState6(false);
1870
- const [tableError, setTableError] = useState6("");
1871
- const [dismissed, setDismissed] = useState6(false);
1872
- if (dismissed) return null;
1873
- const steps = [
1874
- { id: 0, label: "Overview", icon: "\u{1F3E0}" },
1875
- { id: 1, label: "1. Data Source", icon: "\u{1F5C4}\uFE0F" },
1876
- { id: 2, label: "2. Adapter", icon: "\u{1F504}" },
1877
- { id: 3, label: "3. Widget Config", icon: "\u2699\uFE0F" }
1878
- ];
1879
- async function handleReadTables() {
1880
- if (!supabase) {
1881
- setTableError("Supabase client belum tersambung. Pastikan prop supabase sudah diisi.");
1882
- return;
1883
- }
1884
- setLoadingTables(true);
1885
- setTableError("");
1886
- try {
1887
- const { data, error } = await supabase.from("information_schema.tables").select("table_name").eq("table_schema", "public").eq("table_type", "BASE TABLE").order("table_name");
1888
- if (error) throw error;
1889
- setTables((data || []).map((r) => r.table_name));
1890
- } catch (err) {
1891
- try {
1892
- const { data: rpcData } = await supabase.rpc("get_tables");
1893
- if (rpcData) {
1894
- setTables(rpcData.map((r) => r.table_name || r));
1895
- } else {
1896
- setTableError("Tidak dapat membaca tabel. Pastikan RLS mengizinkan akses atau tambahkan policy SELECT untuk anon.");
1897
- }
1898
- } catch {
1899
- setTableError("Gagal membaca tabel dari Supabase: " + ((err == null ? void 0 : err.message) || "Unknown error"));
1900
- }
1901
- } finally {
1902
- setLoadingTables(false);
1903
- }
1904
- }
1905
- function handleDismiss() {
1906
- setDismissed(true);
1907
- if (onDismiss) onDismiss();
1908
- }
1909
- return /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-overlay", role: "dialog", "aria-modal": "true", "aria-label": "Dashboard Setup Wizard" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-modal" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-header" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-header-title" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-logo" }, "\u{1F6E0}\uFE0F"), /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-h2", style: { margin: 0 } }, "Setup Dashboard"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption" }, "Panduan konfigurasi modul @rozaqi02/reusable-dashboard"))), /* @__PURE__ */ React17.createElement(
1910
- "button",
1911
- {
1912
- type: "button",
1913
- className: "rdb-wizard-close",
1914
- onClick: handleDismiss,
1915
- "aria-label": "Tutup wizard",
1916
- title: "Lanjutkan tanpa wizard"
1917
- },
1918
- "\u2715"
1919
- )), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-steps" }, steps.map((s) => /* @__PURE__ */ React17.createElement(
1920
- "button",
1921
- {
1922
- key: s.id,
1923
- type: "button",
1924
- className: `rdb-wizard-step-btn ${step === s.id ? "rdb-wizard-step-active" : ""}`,
1925
- onClick: () => setStep(s.id)
1926
- },
1927
- /* @__PURE__ */ React17.createElement("span", null, s.icon),
1928
- /* @__PURE__ */ React17.createElement("span", null, s.label)
1929
- ))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-body" }, step === 0 && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-section" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-alert" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-alert-icon" }, "\u26A0\uFE0F"), /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { fontWeight: 600 } }, "Dashboard belum terkonfigurasi"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { marginTop: 4 } }, "Ditemukan ", issues.length, " masalah yang perlu diselesaikan sebelum dashboard dapat berjalan."))), issues.length > 0 && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-issues" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 8 } }, "Masalah yang ditemukan:"), issues.map((issue, i) => /* @__PURE__ */ React17.createElement("div", { key: i, className: "rdb-wizard-issue-item" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-issue-icon" }, "\u274C"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-body" }, issue)))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-flow" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 12 } }, "Alur konfigurasi yang perlu dilakukan:"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-flow-steps" }, [
1930
- { num: "1", label: "Data Source", desc: "Tulis fungsi query ke Supabase" },
1931
- { num: "\u2192", label: "", desc: "" },
1932
- { num: "2", label: "Adapter", desc: "Petakan data mentah ke format standar" },
1933
- { num: "\u2192", label: "", desc: "" },
1934
- { num: "3", label: "Widget Config", desc: "Definisikan widget yang ditampilkan" }
1935
- ].map((item, i) => item.num === "\u2192" ? /* @__PURE__ */ React17.createElement("div", { key: i, className: "rdb-wizard-flow-arrow" }, "\u2192") : /* @__PURE__ */ React17.createElement("div", { key: i, className: "rdb-wizard-flow-box" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-flow-num" }, item.num), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-flow-label" }, item.label), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-flow-desc" }, item.desc))))), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 } }, /* @__PURE__ */ React17.createElement("button", { type: "button", className: "rdb-btn rdb-btn-secondary rdb-btn-sm", onClick: handleDismiss }, "Lanjutkan tanpa wizard"), /* @__PURE__ */ React17.createElement("button", { type: "button", className: "rdb-btn rdb-btn-primary rdb-btn-sm", onClick: () => setStep(1) }, "Mulai panduan \u2192"))), step === 1 && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-section" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-step-num" }, "1"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Data Source")), /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)", marginBottom: 12 } }, "Data Source adalah fungsi yang membaca data dari Supabase. Buat file", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, " src/datasources/myDataSource.js"), " dengan isi berikut:"), /* @__PURE__ */ React17.createElement("pre", { className: "rdb-wizard-code" }, `// src/datasources/myDataSource.js
1936
- export function createMySupabaseSource(supabase) {
1866
+ var PRESET_SIGNATURES = {
1867
+ cidika: { name: "Cidika Travel", tables: ["bookings", "packages", "package_locales"] },
1868
+ tokoSepatu: { name: "Toko Sepatu / E-Commerce", tables: ["orders", "products", "customers"] }
1869
+ };
1870
+ function detectPreset(tables) {
1871
+ if (!(tables == null ? void 0 : tables.length)) return null;
1872
+ const s = new Set(tables);
1873
+ if (PRESET_SIGNATURES.cidika.tables.every((t) => s.has(t))) return "cidika";
1874
+ if (PRESET_SIGNATURES.tokoSepatu.tables.every((t) => s.has(t))) return "tokoSepatu";
1875
+ return null;
1876
+ }
1877
+ var SQL_GRANT_SCHEMA = `GRANT SELECT ON information_schema.tables TO anon;
1878
+ GRANT SELECT ON information_schema.columns TO anon;`;
1879
+ var sqlRls = (tbl) => `-- Pilih salah satu sesuai kebutuhan (jalankan di Supabase SQL Editor):
1880
+
1881
+ -- Opsi 1: Untuk DEVELOPMENT \u2014 nonaktifkan RLS (paling cepat)
1882
+ ALTER TABLE public.${tbl} DISABLE ROW LEVEL SECURITY;
1883
+
1884
+ -- Opsi 2: Untuk PRODUCTION \u2014 izinkan semua orang baca
1885
+ ALTER TABLE public.${tbl} ENABLE ROW LEVEL SECURITY;
1886
+ CREATE POLICY "dashboard_read" ON public.${tbl}
1887
+ FOR SELECT TO anon USING (true);
1888
+
1889
+ -- Opsi 3: Untuk PRODUCTION \u2014 hanya user yang login
1890
+ ALTER TABLE public.${tbl} ENABLE ROW LEVEL SECURITY;
1891
+ CREATE POLICY "dashboard_auth" ON public.${tbl}
1892
+ FOR SELECT TO authenticated USING (true);`;
1893
+ function generateCode({
1894
+ tableName,
1895
+ colDate,
1896
+ colStatus,
1897
+ colTotal,
1898
+ colCustomer,
1899
+ colItem,
1900
+ dashTitle,
1901
+ confirmedValue,
1902
+ pendingValue
1903
+ }) {
1904
+ const title = dashTitle || "Dashboard";
1905
+ const confirmed = confirmedValue || "confirmed";
1906
+ const pending = pendingValue || "pending";
1907
+ const custCol = colCustomer || "customer_name";
1908
+ const itemCol = colItem || "-";
1909
+ const totalCol = colTotal || "total";
1910
+ const cols = [
1911
+ "id",
1912
+ colDate,
1913
+ colStatus,
1914
+ totalCol,
1915
+ ...custCol !== "customer_name" ? [custCol] : ["customer_name"],
1916
+ ...itemCol !== "-" ? [itemCol] : []
1917
+ ].filter(Boolean).join(", ");
1918
+ const dataSource = `// src/datasources/myDashboardSource.js
1919
+ // AUTO-GENERATED oleh Setup Wizard @rozaqi02/reusable-dashboard
1920
+
1921
+ export function createMyDashboardSource(supabase) {
1937
1922
  return {
1938
1923
  async fetchDashboardSnapshot({ fromISO, toISO, statusScope }) {
1939
- const { data: orders = [] } = await supabase
1940
- .from("MY_TABLE") // \u2190 ganti nama tabel kamu
1941
- .select("id, created_at, total_price, status, customer_name")
1942
- .gte("created_at", fromISO)
1943
- .lte("created_at", toISO)
1944
- .order("created_at", { ascending: true });
1924
+ const allQ = supabase
1925
+ .from("${tableName}")
1926
+ .select("${cols}")
1927
+ .gte("${colDate}", fromISO)
1928
+ .lte("${colDate}", toISO)
1929
+ .order("${colDate}", { ascending: true });
1945
1930
 
1946
- const { data: recent = [] } = await supabase
1947
- .from("MY_TABLE")
1948
- .select("id, created_at, customer_name, total_price, status")
1949
- .order("created_at", { ascending: false })
1931
+ const recentQ = supabase
1932
+ .from("${tableName}")
1933
+ .select("${cols}")
1934
+ .gte("${colDate}", fromISO)
1935
+ .lte("${colDate}", toISO)
1936
+ .order("${colDate}", { ascending: false })
1950
1937
  .limit(10);
1951
1938
 
1939
+ if (statusScope && statusScope !== "all") {
1940
+ recentQ.eq("${colStatus}", statusScope);
1941
+ }
1942
+
1943
+ const [all, recent] = await Promise.all([allQ, recentQ]);
1952
1944
  return {
1953
- bookings: orders, // WAJIB: nama harus "bookings"
1954
- recent, // WAJIB: nama harus "recent"
1945
+ bookings: all.data || [],
1946
+ recent: recent.data || [],
1955
1947
  packageLocales: [],
1956
1948
  staticCounts: { packages: 0, sections: 0 },
1957
1949
  };
1958
1950
  },
1959
1951
 
1960
- // Opsional: live update
1961
1952
  subscribeLiveUpdate(onEvent) {
1962
- const ch = supabase.channel("my-dashboard-live")
1953
+ const ch = supabase.channel("rdb-${tableName}-live")
1963
1954
  .on("postgres_changes",
1964
- { event: "*", schema: "public", table: "MY_TABLE" }, onEvent)
1955
+ { event: "*", schema: "public", table: "${tableName}" }, onEvent)
1965
1956
  .subscribe();
1966
1957
  return () => supabase.removeChannel(ch);
1967
1958
  },
1968
1959
  };
1969
- }`), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-supabase-reader" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 8 } }, "\u{1F50D} Baca tabel dari Supabase project kamu secara langsung:"), /* @__PURE__ */ React17.createElement(
1970
- "button",
1971
- {
1972
- type: "button",
1973
- className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
1974
- onClick: handleReadTables,
1975
- disabled: loadingTables
1976
- },
1977
- loadingTables ? "Membaca..." : "Tampilkan daftar tabel Supabase"
1978
- ), tableError && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-table-error" }, tableError), tables && tables.length > 0 && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-table-list" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { marginBottom: 6 } }, "Tabel ditemukan (", tables.length, "):"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-table-chips" }, tables.map((t) => /* @__PURE__ */ React17.createElement("span", { key: t, className: "rdb-wizard-chip" }, t))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { marginTop: 8, color: "var(--rdb-text-muted)" } }, "Ganti ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "MY_TABLE"), " di contoh kode di atas dengan nama tabel yang sesuai.")), !supabase && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-table-error" }, "Tambahkan prop ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "supabase"), " ke", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, " <ReusableDashboardView supabase=", "{supabase}", " />"), " untuk mengaktifkan fitur ini.")), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 } }, /* @__PURE__ */ React17.createElement("button", { type: "button", className: "rdb-btn rdb-btn-secondary rdb-btn-sm", onClick: () => setStep(0) }, "\u2190 Kembali"), /* @__PURE__ */ React17.createElement("button", { type: "button", className: "rdb-btn rdb-btn-primary rdb-btn-sm", onClick: () => setStep(2) }, "Lanjut \u2192"))), step === 2 && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-section" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-step-num" }, "2"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Data Adapter")), /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)", marginBottom: 12 } }, "Adapter mengubah data mentah dari Data Source ke format standar yang dimengerti komponen dashboard. Buat file ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "src/adapters/myAdapter.js"), ":"), /* @__PURE__ */ React17.createElement("pre", { className: "rdb-wizard-code" }, `// src/adapters/myAdapter.js
1960
+ }`;
1961
+ const adapter = `// src/adapters/myDashboardAdapter.js
1962
+ // AUTO-GENERATED oleh Setup Wizard @rozaqi02/reusable-dashboard
1963
+
1979
1964
  import { toNumber, buildDayBuckets } from "@rozaqi02/reusable-dashboard";
1980
1965
 
1981
- export function createEmptyMyData() {
1966
+ export function createEmptyMyDashboardData() {
1982
1967
  return {
1983
- stats: { bookingsConfirm: 0, revenueConfirm: 0 },
1984
- charts: {
1985
- dailyTrends: [],
1986
- statusDistribution: [],
1987
- audienceDistribution: [],
1988
- topPackages: [],
1989
- },
1990
- table: { recentBookings: [] },
1968
+ stats: { bookingsConfirm: 0, revenueConfirm: 0, avgRevenue: 0, conversionRate: 0 },
1969
+ charts: { dailyTrends: [], statusDistribution: [], audienceDistribution: [], topPackages: [] },
1970
+ table: { recentBookings: [] },
1991
1971
  };
1992
1972
  }
1993
1973
 
1994
- export function adaptMyData({ raw, range, dateLocale, labels }) {
1995
- if (!raw) return createEmptyMyData();
1974
+ export function adaptMyDashboardData({ raw, range, dateLocale, labels }) {
1975
+ if (!raw) return createEmptyMyDashboardData();
1996
1976
 
1997
- const buckets = buildDayBuckets(range.daysWindow, range.fromISO, dateLocale);
1998
- const dayMap = new Map(buckets.map(b => [b.dateKey, b]));
1977
+ const buckets = buildDayBuckets(range.daysWindow, range.fromISO, dateLocale);
1978
+ const dayMap = new Map(buckets.map(b => [b.dateKey, b]));
1999
1979
  const statusMap = new Map();
2000
1980
  let confirmed = 0, revenue = 0;
2001
1981
 
2002
1982
  (raw.bookings || []).forEach(row => {
2003
- const status = String(row.status || "pending").toLowerCase();
2004
- const amount = toNumber(row.total_price); // \u2190 sesuaikan nama kolom
2005
- const dayKey = String(row.created_at || "").slice(0, 10);
1983
+ const st = String(row["${colStatus}"] || "").toLowerCase();
1984
+ const amt = toNumber(row["${totalCol}"]);
1985
+ const dk = String(row["${colDate}"] || "").slice(0, 10);
2006
1986
 
2007
- statusMap.set(status, (statusMap.get(status) || 0) + 1);
2008
- const bucket = dayMap.get(dayKey);
2009
- if (bucket && status === "confirmed") {
2010
- bucket.count += 1;
2011
- bucket.revenue += amount;
1987
+ statusMap.set(st, (statusMap.get(st) || 0) + 1);
1988
+ const b = dayMap.get(dk);
1989
+ if (b) {
1990
+ if (st === "${confirmed}") { b.count++; b.revenue += amt; }
1991
+ if (st === "${pending}") { b.pendingCount++; }
2012
1992
  }
2013
- if (status === "confirmed") { confirmed++; revenue += amount; }
1993
+ if (st === "${confirmed}") { confirmed++; revenue += amt; }
2014
1994
  });
2015
1995
 
1996
+ const total = (raw.bookings || []).length;
2016
1997
  return {
2017
- stats: { bookingsConfirm: confirmed, revenueConfirm: revenue },
1998
+ stats: {
1999
+ bookingsConfirm: confirmed,
2000
+ revenueConfirm: revenue,
2001
+ avgRevenue: confirmed > 0 ? Math.round(revenue / confirmed) : 0,
2002
+ conversionRate: total > 0 ? Math.round((confirmed / total) * 100) : 0,
2003
+ },
2018
2004
  charts: {
2019
2005
  dailyTrends: buckets,
2020
2006
  statusDistribution: Array.from(statusMap.entries())
2021
- .map(([status, count]) => ({
2022
- status,
2023
- label: labels?.formatStatusLabel?.(status) || status,
2024
- count,
2025
- })),
2007
+ .sort((a, b) => b[1] - a[1])
2008
+ .map(([s, c]) => ({ status: s, count: c, label: labels?.formatStatusLabel?.(s) || s })),
2026
2009
  audienceDistribution: [],
2027
2010
  topPackages: [],
2028
2011
  },
2029
2012
  table: {
2030
2013
  recentBookings: (raw.recent || []).map(row => ({
2031
- id: row.id,
2032
- createdAt: row.created_at,
2033
- customerName: row.customer_name || "-",
2034
- packageName: row.service_type || "-", // \u2190 sesuaikan nama kolom
2014
+ id: row.id,
2015
+ createdAt: row["${colDate}"],
2016
+ customerName: row["${custCol}"] || "-",
2017
+ packageName: ${itemCol !== "-" ? `row["${itemCol}"] || "-"` : '"-"'},
2035
2018
  audienceLabel: "-",
2036
- totalIDR: toNumber(row.total_price),
2037
- status: String(row.status || "pending").toLowerCase(),
2038
- statusLabel: labels?.formatStatusLabel?.(row.status) || row.status,
2019
+ totalIDR: toNumber(row["${totalCol}"]),
2020
+ status: String(row["${colStatus}"] || "").toLowerCase(),
2021
+ statusLabel: labels?.formatStatusLabel?.(row["${colStatus}"]) || row["${colStatus}"] || "-",
2039
2022
  })),
2040
2023
  },
2041
2024
  };
2042
- }`), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-tip" }, "\u{1F4A1} ", /* @__PURE__ */ React17.createElement("strong", null, "Aturan penting:"), " key di ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "stats"), " (mis. ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "bookingsConfirm"), ") harus cocok dengan ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "valueKey"), " di widget config pada Step 3."), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 } }, /* @__PURE__ */ React17.createElement("button", { type: "button", className: "rdb-btn rdb-btn-secondary rdb-btn-sm", onClick: () => setStep(1) }, "\u2190 Kembali"), /* @__PURE__ */ React17.createElement("button", { type: "button", className: "rdb-btn rdb-btn-primary rdb-btn-sm", onClick: () => setStep(3) }, "Lanjut \u2192"))), step === 3 && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-section" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-step-num" }, "3"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Widget Config & Rangkaian Akhir")), /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)", marginBottom: 12 } }, "Widget Config mendefinisikan tampilan dashboard: kartu mana, chart apa, kolom tabel apa. Gunakan ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "createDashboardConfig()"), " untuk mengemas semuanya jadi 1 objek:"), /* @__PURE__ */ React17.createElement("pre", { className: "rdb-wizard-code" }, `// src/pages/MyDashboard.jsx
2043
- import { supabase } from "../lib/supabaseClient";
2044
- import {
2045
- ReusableDashboardView,
2046
- useReusableDashboard,
2047
- createDashboardConfig,
2048
- } from "@rozaqi02/reusable-dashboard";
2025
+ }`;
2026
+ const widgetConfig = `// src/config/myDashboardConfig.js
2027
+ // AUTO-GENERATED oleh Setup Wizard @rozaqi02/reusable-dashboard
2049
2028
 
2050
- import { createMySupabaseSource } from "../datasources/myDataSource";
2051
- import { adaptMyData, createEmptyMyData } from "../adapters/myAdapter";
2052
-
2053
- // 1. Widget Config \u2014 deklaratif, tentukan apa yang tampil
2054
- const myWidgetConfig = {
2055
- id: "my.dashboard",
2056
- defaultFilters: { statusScope: "confirmed", daysPreset: 30 },
2029
+ export const myDashboardConfig = {
2030
+ id: "my.custom.dashboard",
2031
+ defaultFilters: { statusScope: "${confirmed}", daysPreset: 30,
2032
+ sortPkgBy: "bookings", sortPkgDir: "desc" },
2057
2033
  widgets: {
2058
2034
  stats: [
2059
- { id: "orders", label: "confirmedBookings", icon: "TrendingUp",
2060
- valueKey: "bookingsConfirm", format: "number", accentColor: "blue" },
2061
- { id: "revenue", label: "confirmedRevenue", icon: "DollarSign",
2062
- valueKey: "revenueConfirm", format: "currency", accentColor: "green" },
2035
+ { id:"orders", label:"confirmedBookings", icon:"TrendingUp",
2036
+ valueKey:"bookingsConfirm", format:"number", accentColor:"blue" },
2037
+ { id:"revenue", label:"confirmedRevenue", icon:"DollarSign",
2038
+ valueKey:"revenueConfirm", format:"currency", accentColor:"green" },
2039
+ { id:"avg", label:"avgRevenue", icon:"Users",
2040
+ valueKey:"avgRevenue", format:"currency", accentColor:"violet" },
2041
+ { id:"conversion", label:"conversionRate", icon:"PieChart",
2042
+ valueKey:"conversionRate", format:"percent", accentColor:"orange" },
2063
2043
  ],
2064
2044
  charts: [
2065
- { id: "trend", type: "dailyArea", label: "dailyTrends", icon: "BarChart3" },
2066
- { id: "status", type: "statusPie", label: "statusDistribution", icon: "PieChart" },
2045
+ { id:"trend", type:"dailyArea", label:"dailyTrends", icon:"BarChart3" },
2046
+ { id:"status", type:"statusPie", label:"statusDistribution", icon:"PieChart" },
2067
2047
  ],
2068
2048
  table: {
2069
- id: "recent", label: "recentBookings", icon: "Calendar",
2070
- emptyLabel: "noRecentBookings",
2049
+ id:"recentTx", label:"recentBookings", icon:"Calendar", emptyLabel:"noRecentBookings",
2071
2050
  columns: [
2072
- { id: "date", label: "date", accessor: "createdAt", type: "date" },
2073
- { id: "customer", label: "customer", accessor: "customerName" },
2074
- { id: "total", label: "total", accessor: "totalIDR", type: "currency" },
2075
- { id: "status", label: "status", accessor: "statusLabel",
2076
- type: "statusBadge", statusAccessor: "status" },
2051
+ { id:"date", label:"date", accessor:"createdAt", type:"date" },
2052
+ { id:"customer", label:"customer", accessor:"customerName" },${itemCol !== "-" ? `
2053
+ { id:"item", label:"package", accessor:"packageName" },` : ""}
2054
+ { id:"total", label:"total", accessor:"totalIDR", type:"currency" },
2055
+ { id:"status", label:"status", accessor:"statusLabel",
2056
+ type:"statusBadge", statusAccessor:"status" },
2077
2057
  ],
2078
2058
  },
2079
2059
  },
2080
- };
2060
+ };`;
2061
+ const dashboard = `// src/pages/admin/Dashboard.jsx
2062
+ // AUTO-GENERATED oleh Setup Wizard @rozaqi02/reusable-dashboard
2081
2063
 
2082
- // 2. Kemas semua jadi 1 objek (buat di luar komponen, sekali saja)
2083
- const dashConfig = createDashboardConfig({
2084
- widgetConfig: myWidgetConfig,
2085
- dataSource: createMySupabaseSource(supabase),
2086
- adapter: adaptMyData,
2087
- createEmptyState: createEmptyMyData,
2088
- languageCode: "id",
2089
- dateLocale: "id-ID",
2090
- });
2064
+ import React from "react";
2065
+ import { supabase } from "../../lib/supabaseClient.js";
2066
+ import {
2067
+ ReusableDashboardView, useReusableDashboard, createDashboardConfig,
2068
+ } from "@rozaqi02/reusable-dashboard";
2091
2069
 
2092
- // 3. Labels \u2014 teks UI
2093
- const labels = { title: "Dashboard Saya", refresh: "Refresh",
2094
- liveUpdate: "Live", loadFailed: "Gagal memuat.", retry: "Coba Lagi",
2095
- confirmedBookings: "Total Order", confirmedRevenue: "Total Pendapatan",
2096
- dailyTrends: "Tren Harian", statusDistribution: "Distribusi Status",
2097
- recentBookings: "Order Terbaru", noRecentBookings: "Belum ada order",
2098
- date: "Tanggal", customer: "Pelanggan", total: "Total", status: "Status",
2099
- bookingsMetric: "Order", revenueMetric: "Pendapatan",
2100
- confirmedBookingMetric: "Order (Confirmed)",
2101
- confirmedRevenueMetric: "Pendapatan (Confirmed)",
2070
+ // Tiga file hasil generate wizard
2071
+ import { myDashboardConfig } from "../../config/myDashboardConfig";
2072
+ import { createMyDashboardSource } from "../../datasources/myDashboardSource";
2073
+ import { adaptMyDashboardData, createEmptyMyDashboardData } from "../../adapters/myDashboardAdapter";
2074
+
2075
+ const source = createMyDashboardSource(supabase);
2076
+
2077
+ const labels = {
2078
+ title: "${title}", refresh: "Refresh", liveUpdate: "Live update",
2079
+ loadFailed: "Gagal memuat data.", retry: "Coba Lagi",
2080
+ confirmedOnly: "Selesai", pendingOnly: "Proses", allStatus: "Semua Status",
2081
+ showPendingOverlay: "Tampilkan pending", reset: "Reset",
2082
+ confirmedBookings: "Total Transaksi", confirmedRevenue: "Total Pendapatan",
2083
+ avgRevenue: "Rata-rata / Transaksi", conversionRate: "Conversion Rate",
2084
+ dailyTrends: "Tren Harian", statusDistribution: "Distribusi Status",
2085
+ recentBookings: "Transaksi Terbaru", noRecentBookings: "Belum ada transaksi",
2086
+ date: "Tanggal", customer: "Pelanggan", package: "Item",
2087
+ total: "Total", status: "Status",
2088
+ bookingsMetric: "Transaksi", revenueMetric: "Pendapatan",
2089
+ confirmedBookingMetric: "Transaksi (Selesai)",
2090
+ confirmedRevenueMetric: "Pendapatan (Selesai)",
2102
2091
  dayLabel: n => n + " hari",
2103
- formatStatusLabel: s =>
2104
- ({ confirmed: "Selesai", pending: "Proses", cancelled: "Batal" })[s] || s,
2092
+ formatStatusLabel: s => ({ "${confirmed}":"Selesai", "${pending}":"Proses" })[s] || s || "-",
2105
2093
  formatAudienceLabel: v => v || "-",
2106
2094
  };
2107
2095
 
2108
- // 4. Render \u2014 selesai!
2109
- export default function MyDashboard() {
2110
- const state = useReusableDashboard({ ...dashConfig, labels });
2096
+ const dashboardConfig = createDashboardConfig({
2097
+ widgetConfig: myDashboardConfig, dataSource: source,
2098
+ adapter: adaptMyDashboardData, createEmptyState: createEmptyMyDashboardData,
2099
+ languageCode: "id", dateLocale: "id-ID", labels,
2100
+ });
2101
+
2102
+ export default function Dashboard() {
2103
+ const state = useReusableDashboard({ ...dashboardConfig, labels });
2111
2104
  return (
2112
2105
  <ReusableDashboardView
2113
- config={dashConfig.config}
2114
- labels={labels}
2115
- loading={state.loading}
2116
- error={state.error}
2117
- filters={state.filters}
2118
- onFilterChange={state.updateFilter}
2119
- onResetFilters={state.resetFilters}
2120
- onRefresh={state.refresh}
2121
- data={state.data}
2122
- dateLocale={dashConfig.dateLocale}
2106
+ config={dashboardConfig.config} labels={labels}
2107
+ loading={state.loading} error={state.error}
2108
+ filters={state.filters} onFilterChange={state.updateFilter}
2109
+ onResetFilters={state.resetFilters} onRefresh={state.refresh}
2110
+ data={state.data} dateLocale={dashboardConfig.dateLocale}
2123
2111
  liveUpdateEnabled={state.liveUpdateEnabled}
2112
+ supabase={supabase} dashboardConfig={dashboardConfig}
2124
2113
  />
2125
2114
  );
2126
- }`), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-tip" }, "\u2705 ", /* @__PURE__ */ React17.createElement("strong", null, "Selesai!"), " Jalankan ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "npm start"), " dan buka halaman dashboard. Wizard ini tidak akan muncul lagi setelah konfigurasi valid."), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 } }, /* @__PURE__ */ React17.createElement("button", { type: "button", className: "rdb-btn rdb-btn-secondary rdb-btn-sm", onClick: () => setStep(2) }, "\u2190 Kembali"), /* @__PURE__ */ React17.createElement("button", { type: "button", className: "rdb-btn rdb-btn-primary rdb-btn-sm", onClick: handleDismiss }, "Tutup & lanjutkan \u2713"))))));
2115
+ }`;
2116
+ return { dataSource, adapter, widgetConfig, dashboard };
2117
+ }
2118
+ function SetupWizard({ issues = [], onDismiss, supabase }) {
2119
+ var _a, _b;
2120
+ const [dismissed, setDismissed] = useState6(false);
2121
+ const [step, setStep] = useState6(0);
2122
+ const [detecting, setDetecting] = useState6(true);
2123
+ const [tables, setTables] = useState6([]);
2124
+ const [columns, setColumns] = useState6({});
2125
+ const [sampleData, setSampleData] = useState6({});
2126
+ const [statusValues, setStatusValues] = useState6({});
2127
+ const [supabaseOk, setSupabaseOk] = useState6(false);
2128
+ const [detectionDone, setDetectionDone] = useState6(false);
2129
+ const [tableBlocked, setTableBlocked] = useState6(false);
2130
+ const [selectedTable, setSelectedTable] = useState6("");
2131
+ const [colDate, setColDate] = useState6("");
2132
+ const [colStatus, setColStatus] = useState6("");
2133
+ const [colTotal, setColTotal] = useState6("");
2134
+ const [colCustomer, setColCustomer] = useState6("");
2135
+ const [colItem, setColItem] = useState6("");
2136
+ const [confirmedVal, setConfirmedVal] = useState6("");
2137
+ const [pendingVal, setPendingVal] = useState6("");
2138
+ const [dashTitle, setDashTitle] = useState6("Dashboard");
2139
+ const [loadingCols, setLoadingCols] = useState6(false);
2140
+ const [colsBlocked, setColsBlocked] = useState6(false);
2141
+ const [generatedCode, setGeneratedCode] = useState6(null);
2142
+ const [activeTab, setActiveTab] = useState6("dashboard");
2143
+ const [copied, setCopied] = useState6("");
2144
+ const [showRls, setShowRls] = useState6(false);
2145
+ useEffect3(() => {
2146
+ if (!supabase) {
2147
+ setDetecting(false);
2148
+ setDetectionDone(true);
2149
+ return;
2150
+ }
2151
+ (async () => {
2152
+ try {
2153
+ const { data: d1, error: e1 } = await supabase.from("information_schema.tables").select("table_name").eq("table_schema", "public").eq("table_type", "BASE TABLE").order("table_name");
2154
+ if (!e1 && (d1 == null ? void 0 : d1.length)) {
2155
+ setTables(d1.map((r) => r.table_name));
2156
+ setSupabaseOk(true);
2157
+ return;
2158
+ }
2159
+ const { data: d2, error: e2 } = await supabase.rpc("rdb_get_tables");
2160
+ if (!e2 && Array.isArray(d2)) {
2161
+ setTables(d2.map((r) => typeof r === "string" ? r : r.table_name || r));
2162
+ setSupabaseOk(true);
2163
+ return;
2164
+ }
2165
+ const { error: ping } = await supabase.from("_rdb_").select("*").limit(1);
2166
+ const ok = !ping || ping.code === "42P01" || ping.code === "PGRST116" || (ping.message || "").includes("does not exist");
2167
+ setSupabaseOk(ok);
2168
+ if (ok) setTableBlocked(true);
2169
+ } catch {
2170
+ setSupabaseOk(false);
2171
+ } finally {
2172
+ setDetecting(false);
2173
+ setDetectionDone(true);
2174
+ }
2175
+ })();
2176
+ }, [supabase]);
2177
+ const analyzeTable = useCallback5(async (tbl) => {
2178
+ var _a2;
2179
+ if (!supabase || !tbl) return;
2180
+ if (columns[tbl]) {
2181
+ autoDetect(tbl, columns[tbl]);
2182
+ return;
2183
+ }
2184
+ setLoadingCols(true);
2185
+ setColsBlocked(false);
2186
+ try {
2187
+ let cols = null;
2188
+ const { data: ca, error: ea } = await supabase.from("information_schema.columns").select("column_name, data_type").eq("table_schema", "public").eq("table_name", tbl).order("ordinal_position");
2189
+ if (!ea && (ca == null ? void 0 : ca.length)) cols = ca;
2190
+ if (!cols) {
2191
+ const { data: sb, error: eb } = await supabase.from(tbl).select("*").limit(1);
2192
+ if (!eb && (sb == null ? void 0 : sb.length) > 0)
2193
+ cols = Object.keys(sb[0]).map((k) => ({ column_name: k, data_type: typeof sb[0][k] }));
2194
+ }
2195
+ if (cols) {
2196
+ setColumns((p) => ({ ...p, [tbl]: cols }));
2197
+ autoDetect(tbl, cols);
2198
+ } else {
2199
+ setColsBlocked(true);
2200
+ }
2201
+ const { data: sample2 } = await supabase.from(tbl).select("*").limit(5);
2202
+ if ((sample2 == null ? void 0 : sample2.length) > 0) {
2203
+ setSampleData((p) => ({ ...p, [tbl]: sample2 }));
2204
+ const statusCol = (_a2 = (cols || []).find(
2205
+ (c) => ["status", "state", "kondisi", "order_status"].includes(c.column_name.toLowerCase())
2206
+ )) == null ? void 0 : _a2.column_name;
2207
+ if (statusCol) {
2208
+ const uniqVals = [...new Set(sample2.map((r) => r[statusCol]).filter(Boolean))];
2209
+ setStatusValues((p) => ({ ...p, [tbl]: { [statusCol]: uniqVals } }));
2210
+ const vals = uniqVals.map((v) => String(v).toLowerCase());
2211
+ const cfm = uniqVals.find((v) => ["confirmed", "selesai", "lunas", "paid", "done", "completed", "approve"].includes(String(v).toLowerCase()));
2212
+ const pnd = uniqVals.find((v) => ["pending", "proses", "menunggu", "processing", "waiting", "draft"].includes(String(v).toLowerCase()));
2213
+ if (cfm) setConfirmedVal(String(cfm));
2214
+ if (pnd) setPendingVal(String(pnd));
2215
+ }
2216
+ }
2217
+ } catch {
2218
+ setColsBlocked(true);
2219
+ } finally {
2220
+ setLoadingCols(false);
2221
+ }
2222
+ }, [supabase, columns]);
2223
+ function autoDetect(tbl, cols) {
2224
+ const f = (opts) => {
2225
+ var _a2;
2226
+ return ((_a2 = cols.find((c) => opts.includes(c.column_name.toLowerCase()))) == null ? void 0 : _a2.column_name) || "";
2227
+ };
2228
+ setColDate(f(["created_at", "tanggal", "date", "transaction_date", "order_date", "waktu"]));
2229
+ setColStatus(f(["status", "state", "kondisi", "order_status", "payment_status"]));
2230
+ setColTotal(f(["total", "total_idr", "total_amount", "total_price", "harga_total", "amount", "nominal", "harga", "grand_total"]));
2231
+ setColCustomer(f(["customer_name", "nama_pelanggan", "nama", "name", "client_name", "buyer_name", "pelanggan"]));
2232
+ setColItem(f(["service_type", "item_name", "product_name", "package_name", "layanan", "produk", "jenis", "paket"]));
2233
+ }
2234
+ function copyCode(text, key) {
2235
+ var _a2;
2236
+ (_a2 = navigator.clipboard) == null ? void 0 : _a2.writeText(text).then(() => {
2237
+ setCopied(key);
2238
+ setTimeout(() => setCopied(""), 2200);
2239
+ });
2240
+ }
2241
+ function handleDismiss() {
2242
+ setDismissed(true);
2243
+ onDismiss == null ? void 0 : onDismiss();
2244
+ }
2245
+ function handleGenerate() {
2246
+ if (!selectedTable || !colDate || !colStatus || !colTotal) return;
2247
+ setGeneratedCode(generateCode({
2248
+ tableName: selectedTable.trim(),
2249
+ colDate,
2250
+ colStatus,
2251
+ colTotal,
2252
+ colCustomer: colCustomer || "customer_name",
2253
+ colItem: colItem || "-",
2254
+ dashTitle,
2255
+ confirmedValue: confirmedVal || "confirmed",
2256
+ pendingValue: pendingVal || "pending"
2257
+ }));
2258
+ setStep(3);
2259
+ }
2260
+ if (dismissed) return null;
2261
+ const preset = detectPreset(tables);
2262
+ const tblCols = selectedTable ? columns[selectedTable] || [] : [];
2263
+ const colNames = tblCols.map((c) => c.column_name);
2264
+ const sample = selectedTable ? sampleData[selectedTable] || [] : [];
2265
+ const stVals = selectedTable && colStatus ? ((_a = statusValues[selectedTable]) == null ? void 0 : _a[colStatus]) || [] : [];
2266
+ const canGen = selectedTable.trim() && colDate && colStatus && colTotal;
2267
+ const TAB_INFO = {
2268
+ dashboard: { label: "Dashboard.jsx", path: "src/pages/admin/Dashboard.jsx" },
2269
+ dataSource: { label: "myDashboardSource.js", path: "src/datasources/myDashboardSource.js" },
2270
+ adapter: { label: "myDashboardAdapter.js", path: "src/adapters/myDashboardAdapter.js" },
2271
+ widgetConfig: { label: "myDashboardConfig.js", path: "src/config/myDashboardConfig.js" }
2272
+ };
2273
+ function PreviewTable({ rows, cols: previewCols }) {
2274
+ if (!(rows == null ? void 0 : rows.length) || !(previewCols == null ? void 0 : previewCols.length)) return null;
2275
+ const validCols = previewCols.filter((c) => {
2276
+ var _a2;
2277
+ return c && ((_a2 = rows[0]) == null ? void 0 : _a2[c]) !== void 0;
2278
+ });
2279
+ if (!validCols.length) return null;
2280
+ return /* @__PURE__ */ React17.createElement("div", { className: "rdb-table-wrapper", style: { marginTop: 8, fontSize: "0.75rem" } }, /* @__PURE__ */ React17.createElement("table", { className: "rdb-table", style: { fontSize: "0.75rem" } }, /* @__PURE__ */ React17.createElement("thead", null, /* @__PURE__ */ React17.createElement("tr", null, validCols.map((c) => /* @__PURE__ */ React17.createElement("th", { key: c, style: { padding: "4px 8px", fontSize: "0.7rem" } }, c)))), /* @__PURE__ */ React17.createElement("tbody", null, rows.slice(0, 3).map((row, i) => /* @__PURE__ */ React17.createElement("tr", { key: i }, validCols.map((c) => /* @__PURE__ */ React17.createElement("td", { key: c, style: { padding: "3px 8px" } }, String(row[c] ?? "-").slice(0, 30))))))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { marginTop: 4, color: "var(--rdb-text-muted)" } }, "Preview 3 baris data nyata dari database kamu"));
2281
+ }
2282
+ return /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-overlay", role: "dialog", "aria-modal": "true" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-modal" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-header" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-header-title" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-logo" }, "\u{1F6E0}\uFE0F"), /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-h2", style: { margin: 0 } }, "Setup Dashboard Wizard"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption" }, detecting ? "Menghubungi Supabase\u2026" : supabaseOk ? tables.length > 0 ? `\u2705 Tersambung \xB7 ${tables.length} tabel ditemukan` : "\u2705 Tersambung \xB7 ketik nama tabel di Step 2" : "Ikuti langkah di bawah untuk menghubungkan Supabase"))), /* @__PURE__ */ React17.createElement("button", { type: "button", className: "rdb-wizard-close", onClick: handleDismiss }, "\u2715")), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-steps" }, ["1. Koneksi", "2. Pilih Tabel", "3. Konfirmasi", "4. Kode Jadi"].map((lbl, i) => /* @__PURE__ */ React17.createElement(
2283
+ "button",
2284
+ {
2285
+ key: i,
2286
+ type: "button",
2287
+ className: `rdb-wizard-step-btn ${step === i ? "rdb-wizard-step-active" : ""}`,
2288
+ onClick: () => setStep(i)
2289
+ },
2290
+ step > i ? "\u2705 " : "",
2291
+ lbl
2292
+ ))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-body" }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-section" }, step === 0 && /* @__PURE__ */ React17.createElement(React17.Fragment, null, issues.length > 0 && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-issues", style: { marginBottom: 12 } }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 6 } }, "Konfigurasi yang belum lengkap:"), issues.map((iss, i) => /* @__PURE__ */ React17.createElement("div", { key: i, className: "rdb-wizard-issue-item" }, /* @__PURE__ */ React17.createElement("span", null, "\u274C"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-body" }, iss)))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-flow", style: { marginBottom: 16 } }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 10 } }, "\u{1F50D} Status koneksi Supabase:"), detecting ? /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)" } }, "Mendeteksi\u2026") : /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ React17.createElement("span", null, supabaseOk ? "\u2705" : "\u274C"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-body" }, supabaseOk ? "Supabase tersambung" : "Tidak tersambung \u2014 tambahkan prop supabase={supabase} ke <ReusableDashboardView>")), supabaseOk && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ React17.createElement("span", null, tables.length > 0 ? "\u2705" : "\u26A0\uFE0F"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-body" }, tables.length > 0 ? `Wizard membaca ${tables.length} tabel langsung dari database` : "Daftar tabel belum bisa terbaca (butuh izin \u2014 lihat SQL di bawah)")))), supabaseOk && tableBlocked && /* @__PURE__ */ React17.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-alert", style: { marginBottom: 8 } }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-alert-icon" }, "\u{1F511}"), /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { fontWeight: 600 } }, "Agar wizard bisa baca daftar tabel otomatis"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { marginTop: 4 } }, "Jalankan SQL ini ", /* @__PURE__ */ React17.createElement("strong", null, "sekali"), " di Supabase SQL Editor, lalu refresh halaman. Atau lewati dan ketik nama tabel manual di Step 2."))), /* @__PURE__ */ React17.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React17.createElement(
2293
+ "button",
2294
+ {
2295
+ type: "button",
2296
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2297
+ style: { position: "absolute", top: 6, right: 6 },
2298
+ onClick: () => copyCode(SQL_GRANT_SCHEMA, "grant")
2299
+ },
2300
+ copied === "grant" ? "\u2705" : "\u{1F4CB}",
2301
+ " Salin"
2302
+ ), /* @__PURE__ */ React17.createElement("pre", { className: "rdb-wizard-code", style: { fontSize: "0.72rem" } }, SQL_GRANT_SCHEMA))), supabaseOk && /* @__PURE__ */ React17.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ React17.createElement(
2303
+ "button",
2304
+ {
2305
+ type: "button",
2306
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2307
+ onClick: () => setShowRls((r) => !r)
2308
+ },
2309
+ showRls ? "\u25B2 Sembunyikan" : "\u25BC Lihat",
2310
+ " panduan RLS \u2014 agar data tabel bisa terbaca dashboard"
2311
+ ), showRls && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-flow", style: { marginTop: 10 } }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { fontWeight: 600, marginBottom: 6 } }, "Apa itu RLS?"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)", marginBottom: 10 } }, "Row Level Security (RLS) adalah fitur Supabase yang mengontrol siapa yang boleh membaca data. Jika aktif tanpa aturan, dashboard tidak bisa mengambil data apapun."), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 6 } }, "SQL \u2014 ganti ", /* @__PURE__ */ React17.createElement("code", null, "nama_tabel"), " dengan nama tabelmu:"), /* @__PURE__ */ React17.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React17.createElement(
2312
+ "button",
2313
+ {
2314
+ type: "button",
2315
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2316
+ style: { position: "absolute", top: 6, right: 6 },
2317
+ onClick: () => copyCode(sqlRls("nama_tabel"), "rls")
2318
+ },
2319
+ copied === "rls" ? "\u2705" : "\u{1F4CB}",
2320
+ " Salin"
2321
+ ), /* @__PURE__ */ React17.createElement("pre", { className: "rdb-wizard-code", style: { fontSize: "0.7rem" } }, sqlRls("nama_tabel"))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { marginTop: 8, color: "var(--rdb-text-muted)" } }, "\u{1F4A1} ", /* @__PURE__ */ React17.createElement("strong", null, "Development:"), " pakai Opsi 1.", /* @__PURE__ */ React17.createElement("strong", null, " Production:"), " pakai Opsi 2 atau 3."))), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, /* @__PURE__ */ React17.createElement(
2322
+ "button",
2323
+ {
2324
+ type: "button",
2325
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2326
+ onClick: handleDismiss
2327
+ },
2328
+ "Lanjutkan tanpa wizard"
2329
+ ), supabaseOk && /* @__PURE__ */ React17.createElement(
2330
+ "button",
2331
+ {
2332
+ type: "button",
2333
+ className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2334
+ onClick: () => setStep(1)
2335
+ },
2336
+ "Lanjut \u2192 Pilih Tabel"
2337
+ ))), step === 1 && /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-step-num" }, "2"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Pilih Tabel Transaksi")), /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)", marginBottom: 16 } }, "Klik tabel yang berisi data transaksi bisnis kamu. Wizard akan", /* @__PURE__ */ React17.createElement("strong", null, " otomatis membaca kolom dan sample data"), " dari tabel tersebut."), tables.length > 0 && /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 16 } }, tables.map((tbl) => {
2338
+ var _a2, _b2;
2339
+ const isSelected = selectedTable === tbl;
2340
+ const hasSample = ((_a2 = sampleData[tbl]) == null ? void 0 : _a2.length) > 0;
2341
+ const hasColumns = ((_b2 = columns[tbl]) == null ? void 0 : _b2.length) > 0;
2342
+ return /* @__PURE__ */ React17.createElement(
2343
+ "button",
2344
+ {
2345
+ key: tbl,
2346
+ type: "button",
2347
+ onClick: () => {
2348
+ setSelectedTable(tbl);
2349
+ analyzeTable(tbl);
2350
+ },
2351
+ className: `rdb-btn rdb-btn-sm ${isSelected ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2352
+ style: { fontFamily: "monospace", fontSize: "0.82rem" }
2353
+ },
2354
+ isSelected ? "\u2705 " : "",
2355
+ tbl,
2356
+ isSelected && hasColumns ? ` (${columns[tbl].length} kolom)` : ""
2357
+ );
2358
+ })), /* @__PURE__ */ React17.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ React17.createElement("label", { className: "rdb-label" }, tables.length > 0 ? "Atau ketik nama tabel lain:" : "Ketik nama tabel kamu:"), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8 } }, /* @__PURE__ */ React17.createElement(
2359
+ "input",
2360
+ {
2361
+ className: "rdb-input",
2362
+ value: selectedTable,
2363
+ onChange: (e) => setSelectedTable(e.target.value.replace(/[,\s].*/, "")),
2364
+ placeholder: "cth: orders / transaksi / bookings",
2365
+ style: { maxWidth: 280 }
2366
+ }
2367
+ ), /* @__PURE__ */ React17.createElement(
2368
+ "button",
2369
+ {
2370
+ type: "button",
2371
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2372
+ disabled: !selectedTable || loadingCols,
2373
+ onClick: () => analyzeTable(selectedTable)
2374
+ },
2375
+ loadingCols ? "Menganalisis\u2026" : "Analisis tabel"
2376
+ ))), selectedTable && !loadingCols && /* @__PURE__ */ React17.createElement(React17.Fragment, null, colNames.length > 0 && /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-tip", style: { marginBottom: 8 } }, "\u2705 ", /* @__PURE__ */ React17.createElement("strong", null, colNames.length, " kolom"), " ditemukan.", sample.length > 0 && ` ${sample.length} baris sample data terbaca.`, " ", "Kolom sudah otomatis diisi di step berikutnya."), sample.length > 0 && /* @__PURE__ */ React17.createElement(
2377
+ PreviewTable,
2378
+ {
2379
+ rows: sample,
2380
+ cols: [colDate, colStatus, colTotal, colCustomer].filter(Boolean)
2381
+ }
2382
+ )), colsBlocked && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-alert" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-alert-icon" }, "\u2139\uFE0F"), /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { fontWeight: 600 } }, "Kolom tidak bisa terbaca otomatis"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { marginTop: 4 } }, "Kemungkinan RLS aktif. Kamu tetap bisa lanjut dan ketik nama kolom manual di step berikutnya.")))), /* @__PURE__ */ React17.createElement("div", { style: { marginBottom: 12, marginTop: 12 } }, /* @__PURE__ */ React17.createElement("label", { className: "rdb-label" }, "Judul dashboard:"), /* @__PURE__ */ React17.createElement(
2383
+ "input",
2384
+ {
2385
+ className: "rdb-input",
2386
+ value: dashTitle,
2387
+ style: { maxWidth: 280 },
2388
+ onChange: (e) => setDashTitle(e.target.value),
2389
+ placeholder: "cth: Dashboard Laundry / Dashboard Klinik"
2390
+ }
2391
+ )), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, /* @__PURE__ */ React17.createElement(
2392
+ "button",
2393
+ {
2394
+ type: "button",
2395
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2396
+ onClick: () => setStep(0)
2397
+ },
2398
+ "\u2190 Kembali"
2399
+ ), /* @__PURE__ */ React17.createElement(
2400
+ "button",
2401
+ {
2402
+ type: "button",
2403
+ className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2404
+ disabled: !selectedTable || loadingCols,
2405
+ onClick: () => setStep(2)
2406
+ },
2407
+ "Lanjut \u2192 Konfirmasi Kolom"
2408
+ ))), step === 2 && /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-step-num" }, "3"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Konfirmasi \u2014 Tabel: ", /* @__PURE__ */ React17.createElement("code", { style: { fontSize: "0.9em" } }, selectedTable))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)", marginBottom: 16 } }, "Wizard sudah mengisi kolom secara otomatis. Periksa sebentar dan sesuaikan jika ada yang salah."), colNames.length > 0 && /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-supabase-reader", style: { marginBottom: 16 } }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 6 } }, "Semua kolom di tabel ", selectedTable, ":"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-table-chips" }, colNames.map((c) => /* @__PURE__ */ React17.createElement("span", { key: c, className: "rdb-wizard-chip", style: { cursor: "default" } }, c)))), /* @__PURE__ */ React17.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16 } }, [
2409
+ { label: "Kolom Tanggal *", hint: "Tipe timestamp/date", val: colDate, set: setColDate, ph: "created_at" },
2410
+ { label: "Kolom Status *", hint: "Berisi nilai selesai/pending", val: colStatus, set: setColStatus, ph: "status" },
2411
+ { label: "Kolom Total (uang) *", hint: "Integer Rupiah", val: colTotal, set: setColTotal, ph: "total_amount" },
2412
+ { label: "Kolom Nama Pelanggan", hint: "Opsional", val: colCustomer, set: setColCustomer, ph: "customer_name (opsional)" },
2413
+ { label: "Kolom Nama Item / Layanan", hint: "Opsional", val: colItem, set: setColItem, ph: "product_name (opsional)" }
2414
+ ].map(({ label, hint, val, set, ph }) => /* @__PURE__ */ React17.createElement("div", { key: label }, /* @__PURE__ */ React17.createElement("label", { className: "rdb-label" }, label), colNames.length > 0 ? /* @__PURE__ */ React17.createElement("select", { className: "rdb-select", value: val, onChange: (e) => set(e.target.value) }, /* @__PURE__ */ React17.createElement("option", { value: "" }, "\u2014 pilih \u2014"), colNames.map((c) => /* @__PURE__ */ React17.createElement("option", { key: c, value: c }, c))) : /* @__PURE__ */ React17.createElement("input", { className: "rdb-input", value: val, onChange: (e) => set(e.target.value), placeholder: ph }), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { marginTop: 2 } }, hint)))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-flow", style: { marginBottom: 16 } }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 4 } }, "Nilai status yang ditemukan di data kamu:"), stVals.length > 0 ? /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { color: "var(--rdb-text-muted)", marginBottom: 8 } }, "Wizard membaca nilai unik dari kolom ", /* @__PURE__ */ React17.createElement("strong", null, colStatus), ". Klik nilai yang sesuai:"), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 12 } }, stVals.map((v) => /* @__PURE__ */ React17.createElement("div", { key: v, style: { display: "flex", gap: 4, alignItems: "center" } }, /* @__PURE__ */ React17.createElement(
2415
+ "button",
2416
+ {
2417
+ type: "button",
2418
+ className: `rdb-btn rdb-btn-sm ${confirmedVal === String(v) ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2419
+ onClick: () => setConfirmedVal(String(v))
2420
+ },
2421
+ confirmedVal === String(v) ? "\u2705 " : "",
2422
+ "Selesai = ",
2423
+ /* @__PURE__ */ React17.createElement("strong", null, String(v))
2424
+ ), /* @__PURE__ */ React17.createElement(
2425
+ "button",
2426
+ {
2427
+ type: "button",
2428
+ className: `rdb-btn rdb-btn-sm ${pendingVal === String(v) ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2429
+ onClick: () => setPendingVal(String(v))
2430
+ },
2431
+ pendingVal === String(v) ? "\u2705 " : "",
2432
+ "Proses = ",
2433
+ /* @__PURE__ */ React17.createElement("strong", null, String(v))
2434
+ ))))) : /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { color: "var(--rdb-text-muted)", marginBottom: 8 } }, "Nilai status tidak terbaca otomatis. Ketik manual:"), /* @__PURE__ */ React17.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 } }, /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("label", { className: "rdb-label" }, 'Nilai "Selesai/Confirmed"'), /* @__PURE__ */ React17.createElement(
2435
+ "input",
2436
+ {
2437
+ className: "rdb-input",
2438
+ value: confirmedVal,
2439
+ onChange: (e) => setConfirmedVal(e.target.value),
2440
+ placeholder: "confirmed"
2441
+ }
2442
+ )), /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("label", { className: "rdb-label" }, 'Nilai "Pending/Proses"'), /* @__PURE__ */ React17.createElement(
2443
+ "input",
2444
+ {
2445
+ className: "rdb-input",
2446
+ value: pendingVal,
2447
+ onChange: (e) => setPendingVal(e.target.value),
2448
+ placeholder: "pending"
2449
+ }
2450
+ )))), sample.length > 0 && colDate && colStatus && colTotal && /* @__PURE__ */ React17.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 4 } }, "Preview data nyata sesuai mapping kamu:"), /* @__PURE__ */ React17.createElement(
2451
+ PreviewTable,
2452
+ {
2453
+ rows: sample,
2454
+ cols: [colDate, colStatus, colTotal, colCustomer, colItem].filter((c) => c && c !== "-")
2455
+ }
2456
+ )), !canGen && /* @__PURE__ */ React17.createElement("div", { className: "rdb-error-banner", style: { marginBottom: 12 } }, "Isi Kolom Tanggal, Status, dan Total untuk melanjutkan."), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, /* @__PURE__ */ React17.createElement(
2457
+ "button",
2458
+ {
2459
+ type: "button",
2460
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2461
+ onClick: () => setStep(1)
2462
+ },
2463
+ "\u2190 Kembali"
2464
+ ), /* @__PURE__ */ React17.createElement(
2465
+ "button",
2466
+ {
2467
+ type: "button",
2468
+ className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2469
+ disabled: !canGen,
2470
+ onClick: handleGenerate
2471
+ },
2472
+ "\u2728 Generate Kode \u2192"
2473
+ ))), step === 3 && generatedCode && /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-step-num" }, "\u2705"), /* @__PURE__ */ React17.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Kode Sudah Jadi!")), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-tip", style: { marginBottom: 12 } }, /* @__PURE__ */ React17.createElement("strong", null, "4 file digenerate."), " Klik tab file \u2192 Salin \u2192 Simpan ke lokasi yang tertera di bawah tab. Tidak perlu mengubah isi kode \u2014 langsung save dan jalankan."), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-issues", style: { marginBottom: 12 } }, Object.entries(TAB_INFO).map(([key, info], i) => /* @__PURE__ */ React17.createElement("div", { key, className: "rdb-wizard-issue-item" }, /* @__PURE__ */ React17.createElement("span", { style: { color: "var(--rdb-blue-500)", fontWeight: 700, minWidth: 18 } }, i + 1, "."), /* @__PURE__ */ React17.createElement("span", { className: "rdb-body" }, "Salin ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, info.label), " \u2192 simpan ke ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, info.path)))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ React17.createElement("span", { style: { color: "var(--rdb-blue-500)", fontWeight: 700, minWidth: 18 } }, "5."), /* @__PURE__ */ React17.createElement("span", { className: "rdb-body" }, "Di ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "src/index.css"), ", tambahkan:", " ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, '@import "../node_modules/@rozaqi02/reusable-dashboard/dist/index.css";'))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ React17.createElement("span", { style: { color: "var(--rdb-blue-500)", fontWeight: 700, minWidth: 18 } }, "6."), /* @__PURE__ */ React17.createElement("span", { className: "rdb-body" }, "Restart dev server (", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, "npm start"), ") \u2192 buka halaman dashboard \u2192 selesai!"))), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 4, flexWrap: "wrap", marginBottom: 4 } }, Object.entries(TAB_INFO).map(([key, info]) => /* @__PURE__ */ React17.createElement(
2474
+ "button",
2475
+ {
2476
+ key,
2477
+ type: "button",
2478
+ onClick: () => setActiveTab(key),
2479
+ className: `rdb-btn rdb-btn-sm ${activeTab === key ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2480
+ style: { fontFamily: "monospace", fontSize: "0.73rem" }
2481
+ },
2482
+ info.label
2483
+ ))), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { color: "var(--rdb-text-muted)", marginBottom: 6 } }, "\u{1F4C1} Simpan ke: ", /* @__PURE__ */ React17.createElement("code", { className: "rdb-wizard-code-inline" }, (_b = TAB_INFO[activeTab]) == null ? void 0 : _b.path)), /* @__PURE__ */ React17.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React17.createElement(
2484
+ "button",
2485
+ {
2486
+ type: "button",
2487
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2488
+ style: { position: "absolute", top: 8, right: 8, zIndex: 1 },
2489
+ onClick: () => copyCode(generatedCode[activeTab], activeTab)
2490
+ },
2491
+ copied === activeTab ? "\u2705 Tersalin!" : "\u{1F4CB} Salin kode"
2492
+ ), /* @__PURE__ */ React17.createElement("pre", { className: "rdb-wizard-code", style: { maxHeight: 280, overflow: "auto" } }, generatedCode[activeTab])), /* @__PURE__ */ React17.createElement("div", { className: "rdb-wizard-alert", style: { marginTop: 10, marginBottom: 4 } }, /* @__PURE__ */ React17.createElement("span", { className: "rdb-wizard-alert-icon" }, "\u26A0\uFE0F"), /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "rdb-body", style: { fontWeight: 600 } }, "Data tidak muncul di dashboard?"), /* @__PURE__ */ React17.createElement("div", { className: "rdb-caption", style: { marginTop: 4 } }, "Jalankan SQL ini di Supabase SQL Editor:"), /* @__PURE__ */ React17.createElement("div", { style: { position: "relative", marginTop: 6 } }, /* @__PURE__ */ React17.createElement(
2493
+ "button",
2494
+ {
2495
+ type: "button",
2496
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2497
+ style: { position: "absolute", top: 4, right: 4 },
2498
+ onClick: () => copyCode(`ALTER TABLE public.${selectedTable} DISABLE ROW LEVEL SECURITY;`, "rlsquick")
2499
+ },
2500
+ copied === "rlsquick" ? "\u2705" : "\u{1F4CB}"
2501
+ ), /* @__PURE__ */ React17.createElement("pre", { className: "rdb-wizard-code", style: { fontSize: "0.72rem" } }, `ALTER TABLE public.${selectedTable} DISABLE ROW LEVEL SECURITY;`)))), /* @__PURE__ */ React17.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 10 } }, /* @__PURE__ */ React17.createElement(
2502
+ "button",
2503
+ {
2504
+ type: "button",
2505
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2506
+ onClick: () => setStep(2)
2507
+ },
2508
+ "\u2190 Edit mapping"
2509
+ ), /* @__PURE__ */ React17.createElement(
2510
+ "button",
2511
+ {
2512
+ type: "button",
2513
+ className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2514
+ onClick: handleDismiss
2515
+ },
2516
+ "Tutup wizard \u2713"
2517
+ )))))));
2127
2518
  }
2128
2519
  SetupWizard.propTypes = {
2129
2520
  issues: PropTypes17.arrayOf(PropTypes17.string),