@rozaqi02/reusable-dashboard 1.1.4 → 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.cjs CHANGED
@@ -1913,80 +1913,95 @@ var import_prop_types18 = __toESM(require("prop-types"), 1);
1913
1913
  var import_react19 = __toESM(require("react"), 1);
1914
1914
  var import_prop_types17 = __toESM(require("prop-types"), 1);
1915
1915
  var PRESET_SIGNATURES = {
1916
- cidika: {
1917
- name: "Cidika Travel",
1918
- tables: ["bookings", "packages", "package_locales"]
1919
- },
1920
- tokoSepatu: {
1921
- name: "Toko Sepatu / E-Commerce",
1922
- tables: ["orders", "products", "customers"]
1923
- }
1916
+ cidika: { name: "Cidika Travel", tables: ["bookings", "packages", "package_locales"] },
1917
+ tokoSepatu: { name: "Toko Sepatu / E-Commerce", tables: ["orders", "products", "customers"] }
1924
1918
  };
1925
1919
  function detectPreset(tables) {
1926
- if (!tables || tables.length === 0) return null;
1927
- const tSet = new Set(tables);
1928
- if (PRESET_SIGNATURES.cidika.tables.every((t) => tSet.has(t))) return "cidika";
1929
- if (PRESET_SIGNATURES.tokoSepatu.tables.every((t) => tSet.has(t))) return "tokoSepatu";
1920
+ if (!(tables == null ? void 0 : tables.length)) return null;
1921
+ const s = new Set(tables);
1922
+ if (PRESET_SIGNATURES.cidika.tables.every((t) => s.has(t))) return "cidika";
1923
+ if (PRESET_SIGNATURES.tokoSepatu.tables.every((t) => s.has(t))) return "tokoSepatu";
1930
1924
  return null;
1931
1925
  }
1932
- function generateCode(mapping) {
1933
- const {
1934
- tableName,
1935
- colDate,
1936
- colStatus,
1937
- colTotal,
1938
- colCustomer,
1939
- colItem,
1940
- dashTitle,
1941
- confirmedValue,
1942
- pendingValue
1943
- } = mapping;
1944
- const safeTitle = dashTitle || "Dashboard";
1926
+ var SQL_GRANT_SCHEMA = `GRANT SELECT ON information_schema.tables TO anon;
1927
+ GRANT SELECT ON information_schema.columns TO anon;`;
1928
+ var sqlRls = (tbl) => `-- Pilih salah satu sesuai kebutuhan (jalankan di Supabase SQL Editor):
1929
+
1930
+ -- Opsi 1: Untuk DEVELOPMENT \u2014 nonaktifkan RLS (paling cepat)
1931
+ ALTER TABLE public.${tbl} DISABLE ROW LEVEL SECURITY;
1932
+
1933
+ -- Opsi 2: Untuk PRODUCTION \u2014 izinkan semua orang baca
1934
+ ALTER TABLE public.${tbl} ENABLE ROW LEVEL SECURITY;
1935
+ CREATE POLICY "dashboard_read" ON public.${tbl}
1936
+ FOR SELECT TO anon USING (true);
1937
+
1938
+ -- Opsi 3: Untuk PRODUCTION \u2014 hanya user yang login
1939
+ ALTER TABLE public.${tbl} ENABLE ROW LEVEL SECURITY;
1940
+ CREATE POLICY "dashboard_auth" ON public.${tbl}
1941
+ FOR SELECT TO authenticated USING (true);`;
1942
+ function generateCode({
1943
+ tableName,
1944
+ colDate,
1945
+ colStatus,
1946
+ colTotal,
1947
+ colCustomer,
1948
+ colItem,
1949
+ dashTitle,
1950
+ confirmedValue,
1951
+ pendingValue
1952
+ }) {
1953
+ const title = dashTitle || "Dashboard";
1945
1954
  const confirmed = confirmedValue || "confirmed";
1946
1955
  const pending = pendingValue || "pending";
1947
- const safeCustomer = colCustomer || "customer_name";
1948
- const safeItem = colItem || "-";
1949
- const colTotalFull = colTotal || "total";
1956
+ const custCol = colCustomer || "customer_name";
1957
+ const itemCol = colItem || "-";
1958
+ const totalCol = colTotal || "total";
1959
+ const cols = [
1960
+ "id",
1961
+ colDate,
1962
+ colStatus,
1963
+ totalCol,
1964
+ ...custCol !== "customer_name" ? [custCol] : ["customer_name"],
1965
+ ...itemCol !== "-" ? [itemCol] : []
1966
+ ].filter(Boolean).join(", ");
1950
1967
  const dataSource = `// src/datasources/myDashboardSource.js
1951
1968
  // AUTO-GENERATED oleh Setup Wizard @rozaqi02/reusable-dashboard
1952
- // Tabel: ${tableName}
1953
1969
 
1954
1970
  export function createMyDashboardSource(supabase) {
1955
1971
  return {
1956
1972
  async fetchDashboardSnapshot({ fromISO, toISO, statusScope }) {
1957
- const allQuery = supabase
1973
+ const allQ = supabase
1958
1974
  .from("${tableName}")
1959
- .select("id, ${colDate}, ${colStatus}, ${colTotalFull}${colCustomer !== "customer_name" ? `, ${colCustomer}` : ", customer_name"}${colItem !== "-" ? `, ${colItem}` : ""}")
1975
+ .select("${cols}")
1960
1976
  .gte("${colDate}", fromISO)
1961
1977
  .lte("${colDate}", toISO)
1962
1978
  .order("${colDate}", { ascending: true });
1963
1979
 
1964
- const recentQuery = supabase
1980
+ const recentQ = supabase
1965
1981
  .from("${tableName}")
1966
- .select("id, ${colDate}, ${colStatus}, ${colTotalFull}${colCustomer !== "customer_name" ? `, ${colCustomer}` : ", customer_name"}${colItem !== "-" ? `, ${colItem}` : ""}")
1982
+ .select("${cols}")
1967
1983
  .gte("${colDate}", fromISO)
1968
1984
  .lte("${colDate}", toISO)
1969
1985
  .order("${colDate}", { ascending: false })
1970
1986
  .limit(10);
1971
1987
 
1972
1988
  if (statusScope && statusScope !== "all") {
1973
- recentQuery.eq("${colStatus}", statusScope);
1989
+ recentQ.eq("${colStatus}", statusScope);
1974
1990
  }
1975
1991
 
1976
- const [allRes, recentRes] = await Promise.all([allQuery, recentQuery]);
1977
-
1992
+ const [all, recent] = await Promise.all([allQ, recentQ]);
1978
1993
  return {
1979
- bookings: allRes.data || [], // semua transaksi (untuk chart & stats)
1980
- recent: recentRes.data || [], // 10 terbaru (untuk tabel)
1994
+ bookings: all.data || [],
1995
+ recent: recent.data || [],
1981
1996
  packageLocales: [],
1982
1997
  staticCounts: { packages: 0, sections: 0 },
1983
1998
  };
1984
1999
  },
1985
2000
 
1986
2001
  subscribeLiveUpdate(onEvent) {
1987
- const ch = supabase
1988
- .channel("rdb-dashboard-live")
1989
- .on("postgres_changes", { event: "*", schema: "public", table: "${tableName}" }, onEvent)
2002
+ const ch = supabase.channel("rdb-${tableName}-live")
2003
+ .on("postgres_changes",
2004
+ { event: "*", schema: "public", table: "${tableName}" }, onEvent)
1990
2005
  .subscribe();
1991
2006
  return () => supabase.removeChannel(ch);
1992
2007
  },
@@ -1994,15 +2009,13 @@ export function createMyDashboardSource(supabase) {
1994
2009
  }`;
1995
2010
  const adapter = `// src/adapters/myDashboardAdapter.js
1996
2011
  // AUTO-GENERATED oleh Setup Wizard @rozaqi02/reusable-dashboard
1997
- // Mapping kolom: status="${colStatus}", total="${colTotalFull}", tanggal="${colDate}"
1998
2012
 
1999
2013
  import { toNumber, buildDayBuckets } from "@rozaqi02/reusable-dashboard";
2000
2014
 
2001
2015
  export function createEmptyMyDashboardData() {
2002
2016
  return {
2003
- stats: { bookingsConfirm: 0, revenueConfirm: 0 },
2004
- charts: { dailyTrends: [], statusDistribution: [],
2005
- audienceDistribution: [], topPackages: [] },
2017
+ stats: { bookingsConfirm: 0, revenueConfirm: 0, avgRevenue: 0, conversionRate: 0 },
2018
+ charts: { dailyTrends: [], statusDistribution: [], audienceDistribution: [], topPackages: [] },
2006
2019
  table: { recentBookings: [] },
2007
2020
  };
2008
2021
  }
@@ -2016,51 +2029,45 @@ export function adaptMyDashboardData({ raw, range, dateLocale, labels }) {
2016
2029
  let confirmed = 0, revenue = 0;
2017
2030
 
2018
2031
  (raw.bookings || []).forEach(row => {
2019
- const status = String(row["${colStatus}"] || "pending").toLowerCase();
2020
- const amount = toNumber(row["${colTotalFull}"]);
2021
- const dayKey = String(row["${colDate}"] || "").slice(0, 10);
2032
+ const st = String(row["${colStatus}"] || "").toLowerCase();
2033
+ const amt = toNumber(row["${totalCol}"]);
2034
+ const dk = String(row["${colDate}"] || "").slice(0, 10);
2022
2035
 
2023
- statusMap.set(status, (statusMap.get(status) || 0) + 1);
2024
- const bucket = dayMap.get(dayKey);
2025
- if (bucket) {
2026
- if (status === "${confirmed}") { bucket.count++; bucket.revenue += amount; }
2027
- if (status === "${pending}") { bucket.pendingCount++; }
2036
+ statusMap.set(st, (statusMap.get(st) || 0) + 1);
2037
+ const b = dayMap.get(dk);
2038
+ if (b) {
2039
+ if (st === "${confirmed}") { b.count++; b.revenue += amt; }
2040
+ if (st === "${pending}") { b.pendingCount++; }
2028
2041
  }
2029
- if (status === "${confirmed}") { confirmed++; revenue += amount; }
2042
+ if (st === "${confirmed}") { confirmed++; revenue += amt; }
2030
2043
  });
2031
2044
 
2032
- const avgRevenue = confirmed > 0 ? Math.round(revenue / confirmed) : 0;
2033
- const totalTx = (raw.bookings || []).length;
2034
- const conversionRate = totalTx > 0 ? Math.round((confirmed / totalTx) * 100) : 0;
2035
-
2045
+ const total = (raw.bookings || []).length;
2036
2046
  return {
2037
2047
  stats: {
2038
2048
  bookingsConfirm: confirmed,
2039
2049
  revenueConfirm: revenue,
2040
- avgRevenue,
2041
- conversionRate,
2050
+ avgRevenue: confirmed > 0 ? Math.round(revenue / confirmed) : 0,
2051
+ conversionRate: total > 0 ? Math.round((confirmed / total) * 100) : 0,
2042
2052
  },
2043
2053
  charts: {
2044
2054
  dailyTrends: buckets,
2045
2055
  statusDistribution: Array.from(statusMap.entries())
2046
2056
  .sort((a, b) => b[1] - a[1])
2047
- .map(([status, count]) => ({
2048
- status, count,
2049
- label: labels?.formatStatusLabel?.(status) || status,
2050
- })),
2057
+ .map(([s, c]) => ({ status: s, count: c, label: labels?.formatStatusLabel?.(s) || s })),
2051
2058
  audienceDistribution: [],
2052
2059
  topPackages: [],
2053
2060
  },
2054
2061
  table: {
2055
2062
  recentBookings: (raw.recent || []).map(row => ({
2056
- id: row.id,
2057
- createdAt: row["${colDate}"],
2058
- customerName: row["${colCustomer}"] || "-",
2059
- packageName: ${colItem !== "-" ? `row["${colItem}"] || "-"` : '"-"'},
2063
+ id: row.id,
2064
+ createdAt: row["${colDate}"],
2065
+ customerName: row["${custCol}"] || "-",
2066
+ packageName: ${itemCol !== "-" ? `row["${itemCol}"] || "-"` : '"-"'},
2060
2067
  audienceLabel: "-",
2061
- totalIDR: toNumber(row["${colTotalFull}"]),
2062
- status: String(row["${colStatus}"] || "pending").toLowerCase(),
2063
- statusLabel: labels?.formatStatusLabel?.(row["${colStatus}"]) || row["${colStatus}"],
2068
+ totalIDR: toNumber(row["${totalCol}"]),
2069
+ status: String(row["${colStatus}"] || "").toLowerCase(),
2070
+ statusLabel: labels?.formatStatusLabel?.(row["${colStatus}"]) || row["${colStatus}"] || "-",
2064
2071
  })),
2065
2072
  },
2066
2073
  };
@@ -2070,151 +2077,120 @@ export function adaptMyDashboardData({ raw, range, dateLocale, labels }) {
2070
2077
 
2071
2078
  export const myDashboardConfig = {
2072
2079
  id: "my.custom.dashboard",
2073
- defaultFilters: {
2074
- statusScope: "${confirmed}",
2075
- daysPreset: 30,
2076
- sortPkgBy: "bookings",
2077
- sortPkgDir: "desc",
2078
- },
2080
+ defaultFilters: { statusScope: "${confirmed}", daysPreset: 30,
2081
+ sortPkgBy: "bookings", sortPkgDir: "desc" },
2079
2082
  widgets: {
2080
2083
  stats: [
2081
- { id: "orders", label: "confirmedBookings", icon: "TrendingUp",
2082
- valueKey: "bookingsConfirm", format: "number", accentColor: "blue" },
2083
- { id: "revenue", label: "confirmedRevenue", icon: "DollarSign",
2084
- valueKey: "revenueConfirm", format: "currency", accentColor: "green" },
2085
- { id: "avg", label: "avgRevenue", icon: "Users",
2086
- valueKey: "avgRevenue", format: "currency", accentColor: "violet" },
2087
- { id: "conversion", label: "conversionRate", icon: "PieChart",
2088
- valueKey: "conversionRate", format: "percent", accentColor: "orange" },
2084
+ { id:"orders", label:"confirmedBookings", icon:"TrendingUp",
2085
+ valueKey:"bookingsConfirm", format:"number", accentColor:"blue" },
2086
+ { id:"revenue", label:"confirmedRevenue", icon:"DollarSign",
2087
+ valueKey:"revenueConfirm", format:"currency", accentColor:"green" },
2088
+ { id:"avg", label:"avgRevenue", icon:"Users",
2089
+ valueKey:"avgRevenue", format:"currency", accentColor:"violet" },
2090
+ { id:"conversion", label:"conversionRate", icon:"PieChart",
2091
+ valueKey:"conversionRate", format:"percent", accentColor:"orange" },
2089
2092
  ],
2090
2093
  charts: [
2091
- { id: "trend", type: "dailyArea", label: "dailyTrends", icon: "BarChart3" },
2092
- { id: "status", type: "statusPie", label: "statusDistribution", icon: "PieChart" },
2094
+ { id:"trend", type:"dailyArea", label:"dailyTrends", icon:"BarChart3" },
2095
+ { id:"status", type:"statusPie", label:"statusDistribution", icon:"PieChart" },
2093
2096
  ],
2094
2097
  table: {
2095
- id: "recentTx", label: "recentBookings", icon: "Calendar",
2096
- emptyLabel: "noRecentBookings",
2098
+ id:"recentTx", label:"recentBookings", icon:"Calendar", emptyLabel:"noRecentBookings",
2097
2099
  columns: [
2098
- { id: "date", label: "date", accessor: "createdAt", type: "date" },
2099
- { id: "customer", label: "customer", accessor: "customerName" },${colItem !== "-" ? `
2100
- { id: "item", label: "package", accessor: "packageName" },` : ""}
2101
- { id: "total", label: "total", accessor: "totalIDR", type: "currency" },
2102
- { id: "status", label: "status", accessor: "statusLabel",
2103
- type: "statusBadge", statusAccessor: "status" },
2100
+ { id:"date", label:"date", accessor:"createdAt", type:"date" },
2101
+ { id:"customer", label:"customer", accessor:"customerName" },${itemCol !== "-" ? `
2102
+ { id:"item", label:"package", accessor:"packageName" },` : ""}
2103
+ { id:"total", label:"total", accessor:"totalIDR", type:"currency" },
2104
+ { id:"status", label:"status", accessor:"statusLabel",
2105
+ type:"statusBadge", statusAccessor:"status" },
2104
2106
  ],
2105
2107
  },
2106
2108
  },
2107
2109
  };`;
2108
2110
  const dashboard = `// src/pages/admin/Dashboard.jsx
2109
2111
  // AUTO-GENERATED oleh Setup Wizard @rozaqi02/reusable-dashboard
2110
- // Salin file ini ke halaman dashboard kamu.
2111
2112
 
2112
2113
  import React from "react";
2113
2114
  import { supabase } from "../../lib/supabaseClient.js";
2114
2115
  import {
2115
- ReusableDashboardView,
2116
- useReusableDashboard,
2117
- createDashboardConfig,
2116
+ ReusableDashboardView, useReusableDashboard, createDashboardConfig,
2118
2117
  } from "@rozaqi02/reusable-dashboard";
2119
2118
 
2120
- // Import 3 file yang digenerate wizard
2121
- import { myDashboardConfig } from "./myDashboardConfig";
2122
- import { createMyDashboardSource } from "./myDashboardSource";
2123
- import { adaptMyDashboardData, createEmptyMyDashboardData } from "./myDashboardAdapter";
2119
+ // Tiga file hasil generate wizard
2120
+ import { myDashboardConfig } from "../../config/myDashboardConfig";
2121
+ import { createMyDashboardSource } from "../../datasources/myDashboardSource";
2122
+ import { adaptMyDashboardData, createEmptyMyDashboardData } from "../../adapters/myDashboardAdapter";
2124
2123
 
2125
- // Data source \u2014 dibuat sekali di luar komponen
2126
2124
  const source = createMyDashboardSource(supabase);
2127
2125
 
2128
- // Label UI \u2014 sesuaikan teks dengan bahasa/konteks bisnis kamu
2129
2126
  const labels = {
2130
- title: "${safeTitle}",
2131
- refresh: "Refresh",
2132
- liveUpdate: "Live update",
2133
- loadFailed: "Gagal memuat data.",
2134
- retry: "Coba Lagi",
2135
- confirmedOnly: "Selesai",
2136
- pendingOnly: "Proses",
2137
- allStatus: "Semua Status",
2138
- showPendingOverlay: "Tampilkan pending",
2139
- reset: "Reset",
2140
- confirmedBookings: "Total Transaksi",
2141
- confirmedRevenue: "Total Pendapatan",
2142
- avgRevenue: "Rata-rata / Transaksi",
2143
- conversionRate: "Conversion Rate",
2144
- dailyTrends: "Tren Harian",
2145
- statusDistribution: "Distribusi Status",
2146
- recentBookings: "Transaksi Terbaru",
2147
- noRecentBookings: "Belum ada transaksi",
2148
- date: "Tanggal",
2149
- customer: "Pelanggan",
2150
- package: "Item",
2151
- total: "Total",
2152
- status: "Status",
2153
- bookingsMetric: "Transaksi",
2154
- revenueMetric: "Pendapatan",
2127
+ title: "${title}", refresh: "Refresh", liveUpdate: "Live update",
2128
+ loadFailed: "Gagal memuat data.", retry: "Coba Lagi",
2129
+ confirmedOnly: "Selesai", pendingOnly: "Proses", allStatus: "Semua Status",
2130
+ showPendingOverlay: "Tampilkan pending", reset: "Reset",
2131
+ confirmedBookings: "Total Transaksi", confirmedRevenue: "Total Pendapatan",
2132
+ avgRevenue: "Rata-rata / Transaksi", conversionRate: "Conversion Rate",
2133
+ dailyTrends: "Tren Harian", statusDistribution: "Distribusi Status",
2134
+ recentBookings: "Transaksi Terbaru", noRecentBookings: "Belum ada transaksi",
2135
+ date: "Tanggal", customer: "Pelanggan", package: "Item",
2136
+ total: "Total", status: "Status",
2137
+ bookingsMetric: "Transaksi", revenueMetric: "Pendapatan",
2155
2138
  confirmedBookingMetric: "Transaksi (Selesai)",
2156
2139
  confirmedRevenueMetric: "Pendapatan (Selesai)",
2157
2140
  dayLabel: n => n + " hari",
2158
- formatStatusLabel: s =>
2159
- ({ "${confirmed}": "Selesai", "${pending}": "Proses" })[s] || (s || "-"),
2141
+ formatStatusLabel: s => ({ "${confirmed}":"Selesai", "${pending}":"Proses" })[s] || s || "-",
2160
2142
  formatAudienceLabel: v => v || "-",
2161
2143
  };
2162
2144
 
2163
- // Kemas semua jadi 1 objek
2164
2145
  const dashboardConfig = createDashboardConfig({
2165
- widgetConfig: myDashboardConfig,
2166
- dataSource: source,
2167
- adapter: adaptMyDashboardData,
2168
- createEmptyState: createEmptyMyDashboardData,
2169
- languageCode: "id",
2170
- dateLocale: "id-ID",
2171
- labels,
2146
+ widgetConfig: myDashboardConfig, dataSource: source,
2147
+ adapter: adaptMyDashboardData, createEmptyState: createEmptyMyDashboardData,
2148
+ languageCode: "id", dateLocale: "id-ID", labels,
2172
2149
  });
2173
2150
 
2174
2151
  export default function Dashboard() {
2175
2152
  const state = useReusableDashboard({ ...dashboardConfig, labels });
2176
2153
  return (
2177
2154
  <ReusableDashboardView
2178
- config={dashboardConfig.config}
2179
- labels={labels}
2180
- loading={state.loading}
2181
- error={state.error}
2182
- filters={state.filters}
2183
- onFilterChange={state.updateFilter}
2184
- onResetFilters={state.resetFilters}
2185
- onRefresh={state.refresh}
2186
- data={state.data}
2187
- dateLocale={dashboardConfig.dateLocale}
2155
+ config={dashboardConfig.config} labels={labels}
2156
+ loading={state.loading} error={state.error}
2157
+ filters={state.filters} onFilterChange={state.updateFilter}
2158
+ onResetFilters={state.resetFilters} onRefresh={state.refresh}
2159
+ data={state.data} dateLocale={dashboardConfig.dateLocale}
2188
2160
  liveUpdateEnabled={state.liveUpdateEnabled}
2189
- supabase={supabase}
2190
- dashboardConfig={dashboardConfig}
2161
+ supabase={supabase} dashboardConfig={dashboardConfig}
2191
2162
  />
2192
2163
  );
2193
2164
  }`;
2194
2165
  return { dataSource, adapter, widgetConfig, dashboard };
2195
2166
  }
2196
2167
  function SetupWizard({ issues = [], onDismiss, supabase }) {
2168
+ var _a, _b;
2197
2169
  const [dismissed, setDismissed] = (0, import_react19.useState)(false);
2198
2170
  const [step, setStep] = (0, import_react19.useState)(0);
2199
2171
  const [detecting, setDetecting] = (0, import_react19.useState)(true);
2200
2172
  const [tables, setTables] = (0, import_react19.useState)([]);
2201
2173
  const [columns, setColumns] = (0, import_react19.useState)({});
2174
+ const [sampleData, setSampleData] = (0, import_react19.useState)({});
2175
+ const [statusValues, setStatusValues] = (0, import_react19.useState)({});
2202
2176
  const [supabaseOk, setSupabaseOk] = (0, import_react19.useState)(false);
2203
2177
  const [detectionDone, setDetectionDone] = (0, import_react19.useState)(false);
2204
- const [userHasDashboard, setUserHasDashboard] = (0, import_react19.useState)(null);
2178
+ const [tableBlocked, setTableBlocked] = (0, import_react19.useState)(false);
2205
2179
  const [selectedTable, setSelectedTable] = (0, import_react19.useState)("");
2206
2180
  const [colDate, setColDate] = (0, import_react19.useState)("");
2207
2181
  const [colStatus, setColStatus] = (0, import_react19.useState)("");
2208
2182
  const [colTotal, setColTotal] = (0, import_react19.useState)("");
2209
2183
  const [colCustomer, setColCustomer] = (0, import_react19.useState)("");
2210
2184
  const [colItem, setColItem] = (0, import_react19.useState)("");
2211
- const [confirmedVal, setConfirmedVal] = (0, import_react19.useState)("confirmed");
2212
- const [pendingVal, setPendingVal] = (0, import_react19.useState)("pending");
2185
+ const [confirmedVal, setConfirmedVal] = (0, import_react19.useState)("");
2186
+ const [pendingVal, setPendingVal] = (0, import_react19.useState)("");
2213
2187
  const [dashTitle, setDashTitle] = (0, import_react19.useState)("Dashboard");
2214
- const [loadingColumns, setLoadingColumns] = (0, import_react19.useState)(false);
2188
+ const [loadingCols, setLoadingCols] = (0, import_react19.useState)(false);
2189
+ const [colsBlocked, setColsBlocked] = (0, import_react19.useState)(false);
2215
2190
  const [generatedCode, setGeneratedCode] = (0, import_react19.useState)(null);
2216
- const [activeCodeTab, setActiveCodeTab] = (0, import_react19.useState)("dataSource");
2191
+ const [activeTab, setActiveTab] = (0, import_react19.useState)("dashboard");
2217
2192
  const [copied, setCopied] = (0, import_react19.useState)("");
2193
+ const [showRls, setShowRls] = (0, import_react19.useState)(false);
2218
2194
  (0, import_react19.useEffect)(() => {
2219
2195
  if (!supabase) {
2220
2196
  setDetecting(false);
@@ -2223,22 +2199,22 @@ function SetupWizard({ issues = [], onDismiss, supabase }) {
2223
2199
  }
2224
2200
  (async () => {
2225
2201
  try {
2226
- const { data: rpcData, error: rpcErr } = await supabase.rpc("rdb_get_tables");
2227
- if (!rpcErr && Array.isArray(rpcData)) {
2228
- setTables(rpcData.map((r) => typeof r === "string" ? r : r.table_name || r.name || r));
2202
+ 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");
2203
+ if (!e1 && (d1 == null ? void 0 : d1.length)) {
2204
+ setTables(d1.map((r) => r.table_name));
2205
+ setSupabaseOk(true);
2206
+ return;
2207
+ }
2208
+ const { data: d2, error: e2 } = await supabase.rpc("rdb_get_tables");
2209
+ if (!e2 && Array.isArray(d2)) {
2210
+ setTables(d2.map((r) => typeof r === "string" ? r : r.table_name || r));
2229
2211
  setSupabaseOk(true);
2230
- } else {
2231
- const { data: schemaData, error: schemaErr } = await supabase.from("information_schema.tables").select("table_name").eq("table_schema", "public").eq("table_type", "BASE TABLE").order("table_name");
2232
- if (!schemaErr && schemaData) {
2233
- setTables(schemaData.map((r) => r.table_name));
2234
- setSupabaseOk(true);
2235
- } else {
2236
- const { error: pingErr } = await supabase.from("_rdb_ping_").select("*").limit(1);
2237
- const connected = !pingErr || pingErr.code === "42P01" || pingErr.code === "PGRST116" || (pingErr.message || "").includes("does not exist");
2238
- setSupabaseOk(connected);
2239
- setTables([]);
2240
- }
2212
+ return;
2241
2213
  }
2214
+ const { error: ping } = await supabase.from("_rdb_").select("*").limit(1);
2215
+ const ok = !ping || ping.code === "42P01" || ping.code === "PGRST116" || (ping.message || "").includes("does not exist");
2216
+ setSupabaseOk(ok);
2217
+ if (ok) setTableBlocked(true);
2242
2218
  } catch {
2243
2219
  setSupabaseOk(false);
2244
2220
  } finally {
@@ -2247,364 +2223,347 @@ function SetupWizard({ issues = [], onDismiss, supabase }) {
2247
2223
  }
2248
2224
  })();
2249
2225
  }, [supabase]);
2250
- const loadColumns = (0, import_react19.useCallback)(async (tbl) => {
2251
- if (!supabase || !tbl || columns[tbl]) return;
2252
- setLoadingColumns(true);
2226
+ const analyzeTable = (0, import_react19.useCallback)(async (tbl) => {
2227
+ var _a2;
2228
+ if (!supabase || !tbl) return;
2229
+ if (columns[tbl]) {
2230
+ autoDetect(tbl, columns[tbl]);
2231
+ return;
2232
+ }
2233
+ setLoadingCols(true);
2234
+ setColsBlocked(false);
2253
2235
  try {
2254
- const { data: rpcCols, error: rpcErr } = await supabase.rpc("rdb_get_columns", { p_table: tbl });
2255
- if (!rpcErr && Array.isArray(rpcCols)) {
2256
- const colObjs = rpcCols.map(
2257
- (c) => typeof c === "string" ? { column_name: c, data_type: "unknown" } : { column_name: c.column_name || c.name || c, data_type: c.data_type || "unknown" }
2258
- );
2259
- setColumns((prev) => ({ ...prev, [tbl]: colObjs }));
2260
- autoDetectCols(colObjs);
2261
- return;
2236
+ let cols = null;
2237
+ 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");
2238
+ if (!ea && (ca == null ? void 0 : ca.length)) cols = ca;
2239
+ if (!cols) {
2240
+ const { data: sb, error: eb } = await supabase.from(tbl).select("*").limit(1);
2241
+ if (!eb && (sb == null ? void 0 : sb.length) > 0)
2242
+ cols = Object.keys(sb[0]).map((k) => ({ column_name: k, data_type: typeof sb[0][k] }));
2262
2243
  }
2263
- const { data: schemaCols, error: schemaErr } = await supabase.from("information_schema.columns").select("column_name, data_type").eq("table_schema", "public").eq("table_name", tbl).order("ordinal_position");
2264
- if (!schemaErr && schemaCols) {
2265
- setColumns((prev) => ({ ...prev, [tbl]: schemaCols }));
2266
- autoDetectCols(schemaCols);
2267
- return;
2244
+ if (cols) {
2245
+ setColumns((p) => ({ ...p, [tbl]: cols }));
2246
+ autoDetect(tbl, cols);
2247
+ } else {
2248
+ setColsBlocked(true);
2268
2249
  }
2269
- const { data: sampleRow, error: sampleErr } = await supabase.from(tbl).select("*").limit(1);
2270
- if (!sampleErr && sampleRow && sampleRow.length > 0) {
2271
- const colObjs = Object.keys(sampleRow[0]).map((k) => ({
2272
- column_name: k,
2273
- data_type: typeof sampleRow[0][k]
2274
- }));
2275
- setColumns((prev) => ({ ...prev, [tbl]: colObjs }));
2276
- autoDetectCols(colObjs);
2277
- return;
2250
+ const { data: sample2 } = await supabase.from(tbl).select("*").limit(5);
2251
+ if ((sample2 == null ? void 0 : sample2.length) > 0) {
2252
+ setSampleData((p) => ({ ...p, [tbl]: sample2 }));
2253
+ const statusCol = (_a2 = (cols || []).find(
2254
+ (c) => ["status", "state", "kondisi", "order_status"].includes(c.column_name.toLowerCase())
2255
+ )) == null ? void 0 : _a2.column_name;
2256
+ if (statusCol) {
2257
+ const uniqVals = [...new Set(sample2.map((r) => r[statusCol]).filter(Boolean))];
2258
+ setStatusValues((p) => ({ ...p, [tbl]: { [statusCol]: uniqVals } }));
2259
+ const vals = uniqVals.map((v) => String(v).toLowerCase());
2260
+ const cfm = uniqVals.find((v) => ["confirmed", "selesai", "lunas", "paid", "done", "completed", "approve"].includes(String(v).toLowerCase()));
2261
+ const pnd = uniqVals.find((v) => ["pending", "proses", "menunggu", "processing", "waiting", "draft"].includes(String(v).toLowerCase()));
2262
+ if (cfm) setConfirmedVal(String(cfm));
2263
+ if (pnd) setPendingVal(String(pnd));
2264
+ }
2278
2265
  }
2279
- setColumns((prev) => ({ ...prev, [tbl]: [] }));
2280
2266
  } catch {
2281
- setColumns((prev) => ({ ...prev, [tbl]: [] }));
2267
+ setColsBlocked(true);
2282
2268
  } finally {
2283
- setLoadingColumns(false);
2269
+ setLoadingCols(false);
2284
2270
  }
2285
2271
  }, [supabase, columns]);
2286
- function autoDetectCols(colObjs) {
2287
- const find = (candidates) => {
2288
- var _a;
2289
- return ((_a = colObjs.find((c) => candidates.includes(c.column_name.toLowerCase()))) == null ? void 0 : _a.column_name) || "";
2272
+ function autoDetect(tbl, cols) {
2273
+ const f = (opts) => {
2274
+ var _a2;
2275
+ return ((_a2 = cols.find((c) => opts.includes(c.column_name.toLowerCase()))) == null ? void 0 : _a2.column_name) || "";
2290
2276
  };
2291
- setColDate(find(["created_at", "tanggal", "date", "transaction_date", "order_date", "waktu"]));
2292
- setColStatus(find(["status", "state", "kondisi", "order_status", "payment_status"]));
2293
- setColTotal(find(["total", "total_idr", "total_amount", "total_price", "harga_total", "amount", "nominal", "harga", "biaya"]));
2294
- setColCustomer(find(["customer_name", "nama_pelanggan", "nama", "name", "client_name", "buyer_name", "pelanggan"]));
2295
- setColItem(find(["service_type", "item_name", "product_name", "package_name", "layanan", "produk", "nama_layanan", "jenis", "nama_produk"]));
2277
+ setColDate(f(["created_at", "tanggal", "date", "transaction_date", "order_date", "waktu"]));
2278
+ setColStatus(f(["status", "state", "kondisi", "order_status", "payment_status"]));
2279
+ setColTotal(f(["total", "total_idr", "total_amount", "total_price", "harga_total", "amount", "nominal", "harga", "grand_total"]));
2280
+ setColCustomer(f(["customer_name", "nama_pelanggan", "nama", "name", "client_name", "buyer_name", "pelanggan"]));
2281
+ setColItem(f(["service_type", "item_name", "product_name", "package_name", "layanan", "produk", "jenis", "paket"]));
2296
2282
  }
2297
- function copyCode(text, label) {
2298
- navigator.clipboard.writeText(text).then(() => {
2299
- setCopied(label);
2300
- setTimeout(() => setCopied(""), 2e3);
2283
+ function copyCode(text, key) {
2284
+ var _a2;
2285
+ (_a2 = navigator.clipboard) == null ? void 0 : _a2.writeText(text).then(() => {
2286
+ setCopied(key);
2287
+ setTimeout(() => setCopied(""), 2200);
2301
2288
  });
2302
2289
  }
2303
2290
  function handleDismiss() {
2304
2291
  setDismissed(true);
2305
- if (onDismiss) onDismiss();
2292
+ onDismiss == null ? void 0 : onDismiss();
2306
2293
  }
2307
2294
  function handleGenerate() {
2308
2295
  if (!selectedTable || !colDate || !colStatus || !colTotal) return;
2309
- const code = generateCode({
2310
- tableName: selectedTable,
2296
+ setGeneratedCode(generateCode({
2297
+ tableName: selectedTable.trim(),
2311
2298
  colDate,
2312
2299
  colStatus,
2313
2300
  colTotal,
2314
2301
  colCustomer: colCustomer || "customer_name",
2315
2302
  colItem: colItem || "-",
2316
2303
  dashTitle,
2317
- confirmedValue: confirmedVal,
2318
- pendingValue: pendingVal
2319
- });
2320
- setGeneratedCode(code);
2304
+ confirmedValue: confirmedVal || "confirmed",
2305
+ pendingValue: pendingVal || "pending"
2306
+ }));
2321
2307
  setStep(3);
2322
2308
  }
2323
2309
  if (dismissed) return null;
2324
- const detectedPreset = detectPreset(tables);
2325
- const tableColumns = selectedTable ? columns[selectedTable] || [] : [];
2326
- const colNames = tableColumns.map((c) => c.column_name);
2327
- const mappingValid = selectedTable && colDate && colStatus && colTotal;
2328
- const stepLabels = ["Deteksi", "Pilih Tabel", "Mapping Kolom", "Kode Siap Pakai"];
2329
- return /* @__PURE__ */ import_react19.default.createElement(
2330
- "div",
2310
+ const preset = detectPreset(tables);
2311
+ const tblCols = selectedTable ? columns[selectedTable] || [] : [];
2312
+ const colNames = tblCols.map((c) => c.column_name);
2313
+ const sample = selectedTable ? sampleData[selectedTable] || [] : [];
2314
+ const stVals = selectedTable && colStatus ? ((_a = statusValues[selectedTable]) == null ? void 0 : _a[colStatus]) || [] : [];
2315
+ const canGen = selectedTable.trim() && colDate && colStatus && colTotal;
2316
+ const TAB_INFO = {
2317
+ dashboard: { label: "Dashboard.jsx", path: "src/pages/admin/Dashboard.jsx" },
2318
+ dataSource: { label: "myDashboardSource.js", path: "src/datasources/myDashboardSource.js" },
2319
+ adapter: { label: "myDashboardAdapter.js", path: "src/adapters/myDashboardAdapter.js" },
2320
+ widgetConfig: { label: "myDashboardConfig.js", path: "src/config/myDashboardConfig.js" }
2321
+ };
2322
+ function PreviewTable({ rows, cols: previewCols }) {
2323
+ if (!(rows == null ? void 0 : rows.length) || !(previewCols == null ? void 0 : previewCols.length)) return null;
2324
+ const validCols = previewCols.filter((c) => {
2325
+ var _a2;
2326
+ return c && ((_a2 = rows[0]) == null ? void 0 : _a2[c]) !== void 0;
2327
+ });
2328
+ if (!validCols.length) return null;
2329
+ return /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-table-wrapper", style: { marginTop: 8, fontSize: "0.75rem" } }, /* @__PURE__ */ import_react19.default.createElement("table", { className: "rdb-table", style: { fontSize: "0.75rem" } }, /* @__PURE__ */ import_react19.default.createElement("thead", null, /* @__PURE__ */ import_react19.default.createElement("tr", null, validCols.map((c) => /* @__PURE__ */ import_react19.default.createElement("th", { key: c, style: { padding: "4px 8px", fontSize: "0.7rem" } }, c)))), /* @__PURE__ */ import_react19.default.createElement("tbody", null, rows.slice(0, 3).map((row, i) => /* @__PURE__ */ import_react19.default.createElement("tr", { key: i }, validCols.map((c) => /* @__PURE__ */ import_react19.default.createElement("td", { key: c, style: { padding: "3px 8px" } }, String(row[c] ?? "-").slice(0, 30))))))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 4, color: "var(--rdb-text-muted)" } }, "Preview 3 baris data nyata dari database kamu"));
2330
+ }
2331
+ return /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-overlay", role: "dialog", "aria-modal": "true" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-modal" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-header" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-header-title" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-logo" }, "\u{1F6E0}\uFE0F"), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-h2", style: { margin: 0 } }, "Setup Dashboard Wizard"), /* @__PURE__ */ import_react19.default.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__ */ import_react19.default.createElement("button", { type: "button", className: "rdb-wizard-close", onClick: handleDismiss }, "\u2715")), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-steps" }, ["1. Koneksi", "2. Pilih Tabel", "3. Konfirmasi", "4. Kode Jadi"].map((lbl, i) => /* @__PURE__ */ import_react19.default.createElement(
2332
+ "button",
2331
2333
  {
2332
- className: "rdb-wizard-overlay",
2333
- role: "dialog",
2334
- "aria-modal": "true",
2335
- "aria-label": "Setup Wizard @rozaqi02/reusable-dashboard"
2334
+ key: i,
2335
+ type: "button",
2336
+ className: `rdb-wizard-step-btn ${step === i ? "rdb-wizard-step-active" : ""}`,
2337
+ onClick: () => setStep(i)
2336
2338
  },
2337
- /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-modal" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-header" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-header-title" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-logo" }, "\u{1F6E0}\uFE0F"), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-h2", style: { margin: 0 } }, "Setup Dashboard"), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption" }, detecting ? "Mendeteksi kondisi project\u2026" : supabaseOk ? `Supabase tersambung \xB7 ${tables.length} tabel ditemukan` : "Panduan konfigurasi @rozaqi02/reusable-dashboard"))), /* @__PURE__ */ import_react19.default.createElement(
2338
- "button",
2339
- {
2340
- type: "button",
2341
- className: "rdb-wizard-close",
2342
- onClick: handleDismiss,
2343
- title: "Tutup"
2344
- },
2345
- "\u2715"
2346
- )), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-steps" }, stepLabels.map((label, i) => /* @__PURE__ */ import_react19.default.createElement(
2347
- "button",
2348
- {
2349
- key: i,
2350
- type: "button",
2351
- className: `rdb-wizard-step-btn ${step === i ? "rdb-wizard-step-active" : ""}`,
2352
- onClick: () => {
2353
- if (i < step || i === step + 1 && step < 2) setStep(i);
2354
- }
2355
- },
2356
- /* @__PURE__ */ import_react19.default.createElement("span", null, step > i ? "\u2705" : i + 1, "."),
2357
- /* @__PURE__ */ import_react19.default.createElement("span", null, label)
2358
- ))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-body" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-section" }, step === 0 && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, issues.length > 0 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issues", style: { marginBottom: 12 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 6 } }, "Konfigurasi belum lengkap:"), issues.map((iss, i) => /* @__PURE__ */ import_react19.default.createElement("div", { key: i, className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", null, "\u274C"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, iss)))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-flow", style: { marginBottom: 12 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 10 } }, "\u{1F50D} Hasil deteksi otomatis:"), detecting ? /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)" } }, "Mendeteksi\u2026") : /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", null, supabaseOk ? "\u2705" : "\u274C"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, "Supabase ", supabaseOk ? "tersambung" : "tidak tersambung \u2014 pastikan supabaseClient.js sudah dikonfigurasi dan prop supabase dikirim ke ReusableDashboardView")), supabaseOk && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", null, tables.length > 0 ? "\u2705" : "\u26A0\uFE0F"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, tables.length > 0 ? `${tables.length} tabel public ditemukan` : "Daftar tabel tidak bisa dibaca otomatis (RLS aktif) \u2014 kamu bisa ketik nama tabel di step berikutnya")), supabaseOk && tables.length > 0 && detectedPreset && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", null, "\u2705"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, "Tabel cocok dengan preset ", /* @__PURE__ */ import_react19.default.createElement("strong", null, PRESET_SIGNATURES[detectedPreset].name))))), !supabaseOk && detectionDone && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-alert", style: { marginBottom: 12 } }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-alert-icon" }, "\u2139\uFE0F"), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { fontWeight: 600 } }, "Aktifkan koneksi Supabase"), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 4 } }, "Tambahkan prop ke ReusableDashboardView:", " ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, "supabase=", "{supabase}")))), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, /* @__PURE__ */ import_react19.default.createElement(
2359
- "button",
2360
- {
2361
- type: "button",
2362
- className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2363
- onClick: handleDismiss
2364
- },
2365
- "Lanjutkan tanpa wizard"
2366
- ), supabaseOk && /* @__PURE__ */ import_react19.default.createElement(
2367
- "button",
2368
- {
2369
- type: "button",
2370
- className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2371
- onClick: () => setStep(1)
2372
- },
2373
- "Lanjut \u2192 Pilih Tabel"
2374
- ))), step === 1 && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-step-num" }, "2"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Pilih Tabel Transaksi")), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)", marginBottom: 16 } }, "Pilih tabel yang berisi data transaksi bisnis kamu."), tables.length > 0 && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 8 } }, "Tabel yang ditemukan:"), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginBottom: 16 } }, tables.map((tbl) => /* @__PURE__ */ import_react19.default.createElement(
2339
+ step > i ? "\u2705 " : "",
2340
+ lbl
2341
+ ))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-body" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-section" }, step === 0 && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, issues.length > 0 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issues", style: { marginBottom: 12 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 6 } }, "Konfigurasi yang belum lengkap:"), issues.map((iss, i) => /* @__PURE__ */ import_react19.default.createElement("div", { key: i, className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", null, "\u274C"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, iss)))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-flow", style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 10 } }, "\u{1F50D} Status koneksi Supabase:"), detecting ? /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)" } }, "Mendeteksi\u2026") : /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", null, supabaseOk ? "\u2705" : "\u274C"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, supabaseOk ? "Supabase tersambung" : "Tidak tersambung \u2014 tambahkan prop supabase={supabase} ke <ReusableDashboardView>")), supabaseOk && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", null, tables.length > 0 ? "\u2705" : "\u26A0\uFE0F"), /* @__PURE__ */ import_react19.default.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__ */ import_react19.default.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-alert", style: { marginBottom: 8 } }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-alert-icon" }, "\u{1F511}"), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { fontWeight: 600 } }, "Agar wizard bisa baca daftar tabel otomatis"), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 4 } }, "Jalankan SQL ini ", /* @__PURE__ */ import_react19.default.createElement("strong", null, "sekali"), " di Supabase SQL Editor, lalu refresh halaman. Atau lewati dan ketik nama tabel manual di Step 2."))), /* @__PURE__ */ import_react19.default.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ import_react19.default.createElement(
2342
+ "button",
2343
+ {
2344
+ type: "button",
2345
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2346
+ style: { position: "absolute", top: 6, right: 6 },
2347
+ onClick: () => copyCode(SQL_GRANT_SCHEMA, "grant")
2348
+ },
2349
+ copied === "grant" ? "\u2705" : "\u{1F4CB}",
2350
+ " Salin"
2351
+ ), /* @__PURE__ */ import_react19.default.createElement("pre", { className: "rdb-wizard-code", style: { fontSize: "0.72rem" } }, SQL_GRANT_SCHEMA))), supabaseOk && /* @__PURE__ */ import_react19.default.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement(
2352
+ "button",
2353
+ {
2354
+ type: "button",
2355
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2356
+ onClick: () => setShowRls((r) => !r)
2357
+ },
2358
+ showRls ? "\u25B2 Sembunyikan" : "\u25BC Lihat",
2359
+ " panduan RLS \u2014 agar data tabel bisa terbaca dashboard"
2360
+ ), showRls && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-flow", style: { marginTop: 10 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { fontWeight: 600, marginBottom: 6 } }, "Apa itu RLS?"), /* @__PURE__ */ import_react19.default.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__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 6 } }, "SQL \u2014 ganti ", /* @__PURE__ */ import_react19.default.createElement("code", null, "nama_tabel"), " dengan nama tabelmu:"), /* @__PURE__ */ import_react19.default.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ import_react19.default.createElement(
2361
+ "button",
2362
+ {
2363
+ type: "button",
2364
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2365
+ style: { position: "absolute", top: 6, right: 6 },
2366
+ onClick: () => copyCode(sqlRls("nama_tabel"), "rls")
2367
+ },
2368
+ copied === "rls" ? "\u2705" : "\u{1F4CB}",
2369
+ " Salin"
2370
+ ), /* @__PURE__ */ import_react19.default.createElement("pre", { className: "rdb-wizard-code", style: { fontSize: "0.7rem" } }, sqlRls("nama_tabel"))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 8, color: "var(--rdb-text-muted)" } }, "\u{1F4A1} ", /* @__PURE__ */ import_react19.default.createElement("strong", null, "Development:"), " pakai Opsi 1.", /* @__PURE__ */ import_react19.default.createElement("strong", null, " Production:"), " pakai Opsi 2 atau 3."))), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, /* @__PURE__ */ import_react19.default.createElement(
2371
+ "button",
2372
+ {
2373
+ type: "button",
2374
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2375
+ onClick: handleDismiss
2376
+ },
2377
+ "Lanjutkan tanpa wizard"
2378
+ ), supabaseOk && /* @__PURE__ */ import_react19.default.createElement(
2379
+ "button",
2380
+ {
2381
+ type: "button",
2382
+ className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2383
+ onClick: () => setStep(1)
2384
+ },
2385
+ "Lanjut \u2192 Pilih Tabel"
2386
+ ))), step === 1 && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-step-num" }, "2"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Pilih Tabel Transaksi")), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)", marginBottom: 16 } }, "Klik tabel yang berisi data transaksi bisnis kamu. Wizard akan", /* @__PURE__ */ import_react19.default.createElement("strong", null, " otomatis membaca kolom dan sample data"), " dari tabel tersebut."), tables.length > 0 && /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 16 } }, tables.map((tbl) => {
2387
+ var _a2, _b2;
2388
+ const isSelected = selectedTable === tbl;
2389
+ const hasSample = ((_a2 = sampleData[tbl]) == null ? void 0 : _a2.length) > 0;
2390
+ const hasColumns = ((_b2 = columns[tbl]) == null ? void 0 : _b2.length) > 0;
2391
+ return /* @__PURE__ */ import_react19.default.createElement(
2375
2392
  "button",
2376
2393
  {
2377
2394
  key: tbl,
2378
2395
  type: "button",
2379
2396
  onClick: () => {
2380
2397
  setSelectedTable(tbl);
2381
- loadColumns(tbl);
2398
+ analyzeTable(tbl);
2382
2399
  },
2383
- className: `rdb-btn rdb-btn-sm ${selectedTable === tbl ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2384
- style: { justifyContent: "flex-start", fontFamily: "monospace" }
2385
- },
2386
- selectedTable === tbl ? "\u2705 " : "\u25CB ",
2387
- tbl
2388
- )))), tables.length === 0 && /* @__PURE__ */ import_react19.default.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-alert", style: { marginBottom: 12 } }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-alert-icon" }, "\u2139\uFE0F"), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { fontWeight: 600 } }, "Daftar tabel tidak bisa terbaca otomatis"), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 4 } }, "Ini normal \u2014 Supabase memblokir akses ke ", /* @__PURE__ */ import_react19.default.createElement("code", null, "information_schema"), " via API publik. Ketik nama tabel secara manual di bawah."))), /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, "Nama tabel transaksi kamu"), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8 } }, /* @__PURE__ */ import_react19.default.createElement(
2389
- "input",
2390
- {
2391
- className: "rdb-input",
2392
- value: selectedTable,
2393
- onChange: (e) => setSelectedTable(e.target.value),
2394
- placeholder: "cth: orders, transaksi, bookings, penjualan",
2395
- style: { maxWidth: 320 }
2396
- }
2397
- ), /* @__PURE__ */ import_react19.default.createElement(
2398
- "button",
2399
- {
2400
- type: "button",
2401
- className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2402
- disabled: !selectedTable || loadingColumns,
2403
- onClick: () => loadColumns(selectedTable)
2404
- },
2405
- loadingColumns ? "Membaca\u2026" : "Baca kolom"
2406
- ))), tables.length > 0 && !selectedTable && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { color: "var(--rdb-text-muted)", marginBottom: 12 } }, "Atau ketik nama tabel secara manual:", /* @__PURE__ */ import_react19.default.createElement(
2407
- "input",
2408
- {
2409
- className: "rdb-input",
2410
- style: { marginTop: 6, maxWidth: 280 },
2411
- placeholder: "nama tabel lain",
2412
- onChange: (e) => {
2413
- setSelectedTable(e.target.value);
2414
- if (e.target.value) loadColumns(e.target.value);
2415
- }
2416
- }
2417
- )), selectedTable && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-tip", style: { marginBottom: 12 } }, "Tabel ", /* @__PURE__ */ import_react19.default.createElement("strong", null, selectedTable), " dipilih.", loadingColumns ? " Membaca kolom\u2026" : tableColumns.length > 0 ? ` ${tableColumns.length} kolom ditemukan \u2014 kolom sudah ter-auto-detect di step berikutnya.` : tableColumns.length === 0 && !loadingColumns && columns[selectedTable] !== void 0 ? " Tabel kosong atau tidak ada kolom terbaca \u2014 kamu bisa ketik nama kolom manual di step berikutnya." : ""), /* @__PURE__ */ import_react19.default.createElement("div", { style: { marginBottom: 12 } }, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, "Judul dashboard (opsional)"), /* @__PURE__ */ import_react19.default.createElement(
2418
- "input",
2419
- {
2420
- className: "rdb-input",
2421
- value: dashTitle,
2422
- style: { maxWidth: 280 },
2423
- onChange: (e) => setDashTitle(e.target.value),
2424
- placeholder: "cth: Dashboard Laundry Bersih"
2425
- }
2426
- )), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, /* @__PURE__ */ import_react19.default.createElement(
2427
- "button",
2428
- {
2429
- type: "button",
2430
- className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2431
- onClick: () => setStep(0)
2432
- },
2433
- "\u2190 Kembali"
2434
- ), /* @__PURE__ */ import_react19.default.createElement(
2435
- "button",
2436
- {
2437
- type: "button",
2438
- className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2439
- disabled: !selectedTable || loadingColumns,
2440
- onClick: () => setStep(2)
2441
- },
2442
- "Lanjut \u2192 Mapping Kolom"
2443
- ))), step === 2 && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-step-num" }, "3"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Mapping Kolom \u2014 Tabel: ", /* @__PURE__ */ import_react19.default.createElement("code", { style: { fontSize: "0.9rem" } }, selectedTable))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { color: "var(--rdb-text-muted)", marginBottom: 16 } }, "Pilih kolom yang berperan sebagai masing-masing data. Kolom sudah ter-deteksi otomatis \u2014 cukup verifikasi dan sesuaikan jika perlu."), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, "Kolom Tanggal ", /* @__PURE__ */ import_react19.default.createElement("span", { style: { color: "red" } }, "*")), colNames.length > 0 ? /* @__PURE__ */ import_react19.default.createElement(
2444
- "select",
2445
- {
2446
- className: "rdb-select",
2447
- value: colDate,
2448
- onChange: (e) => setColDate(e.target.value)
2400
+ className: `rdb-btn rdb-btn-sm ${isSelected ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2401
+ style: { fontFamily: "monospace", fontSize: "0.82rem" }
2449
2402
  },
2450
- /* @__PURE__ */ import_react19.default.createElement("option", { value: "" }, "\u2014 pilih kolom \u2014"),
2451
- colNames.map((c) => /* @__PURE__ */ import_react19.default.createElement("option", { key: c, value: c }, c))
2452
- ) : /* @__PURE__ */ import_react19.default.createElement(
2453
- "input",
2454
- {
2455
- className: "rdb-input",
2456
- value: colDate,
2457
- onChange: (e) => setColDate(e.target.value),
2458
- placeholder: "cth: created_at"
2459
- }
2460
- ), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 2 } }, "Tipe timestamp/date")), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, "Kolom Status ", /* @__PURE__ */ import_react19.default.createElement("span", { style: { color: "red" } }, "*")), colNames.length > 0 ? /* @__PURE__ */ import_react19.default.createElement(
2461
- "select",
2462
- {
2463
- className: "rdb-select",
2464
- value: colStatus,
2465
- onChange: (e) => setColStatus(e.target.value)
2466
- },
2467
- /* @__PURE__ */ import_react19.default.createElement("option", { value: "" }, "\u2014 pilih kolom \u2014"),
2468
- colNames.map((c) => /* @__PURE__ */ import_react19.default.createElement("option", { key: c, value: c }, c))
2469
- ) : /* @__PURE__ */ import_react19.default.createElement(
2470
- "input",
2471
- {
2472
- className: "rdb-input",
2473
- value: colStatus,
2474
- onChange: (e) => setColStatus(e.target.value),
2475
- placeholder: "cth: status"
2476
- }
2477
- ), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 2 } }, "Nilai selesai/pending")), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, "Kolom Total (uang) ", /* @__PURE__ */ import_react19.default.createElement("span", { style: { color: "red" } }, "*")), colNames.length > 0 ? /* @__PURE__ */ import_react19.default.createElement(
2478
- "select",
2479
- {
2480
- className: "rdb-select",
2481
- value: colTotal,
2482
- onChange: (e) => setColTotal(e.target.value)
2483
- },
2484
- /* @__PURE__ */ import_react19.default.createElement("option", { value: "" }, "\u2014 pilih kolom \u2014"),
2485
- colNames.map((c) => /* @__PURE__ */ import_react19.default.createElement("option", { key: c, value: c }, c))
2486
- ) : /* @__PURE__ */ import_react19.default.createElement(
2487
- "input",
2488
- {
2489
- className: "rdb-input",
2490
- value: colTotal,
2491
- onChange: (e) => setColTotal(e.target.value),
2492
- placeholder: "cth: total_amount"
2493
- }
2494
- ), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 2 } }, "Integer Rupiah")), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, "Kolom Nama Pelanggan"), colNames.length > 0 ? /* @__PURE__ */ import_react19.default.createElement(
2495
- "select",
2496
- {
2497
- className: "rdb-select",
2498
- value: colCustomer,
2499
- onChange: (e) => setColCustomer(e.target.value)
2500
- },
2501
- /* @__PURE__ */ import_react19.default.createElement("option", { value: "" }, "\u2014 opsional \u2014"),
2502
- colNames.map((c) => /* @__PURE__ */ import_react19.default.createElement("option", { key: c, value: c }, c))
2503
- ) : /* @__PURE__ */ import_react19.default.createElement(
2504
- "input",
2505
- {
2506
- className: "rdb-input",
2507
- value: colCustomer,
2508
- onChange: (e) => setColCustomer(e.target.value),
2509
- placeholder: "cth: customer_name (opsional)"
2510
- }
2511
- )), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, "Kolom Nama Item/Layanan"), colNames.length > 0 ? /* @__PURE__ */ import_react19.default.createElement(
2512
- "select",
2513
- {
2514
- className: "rdb-select",
2515
- value: colItem,
2516
- onChange: (e) => setColItem(e.target.value)
2517
- },
2518
- /* @__PURE__ */ import_react19.default.createElement("option", { value: "" }, "\u2014 opsional \u2014"),
2519
- colNames.map((c) => /* @__PURE__ */ import_react19.default.createElement("option", { key: c, value: c }, c))
2520
- ) : /* @__PURE__ */ import_react19.default.createElement(
2521
- "input",
2522
- {
2523
- className: "rdb-input",
2524
- value: colItem,
2525
- onChange: (e) => setColItem(e.target.value),
2526
- placeholder: "cth: service_type (opsional)"
2527
- }
2528
- ))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-flow", style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 8 } }, "Nilai status di tabel kamu:"), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 } }, /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, 'Nilai "Selesai/Confirmed"'), /* @__PURE__ */ import_react19.default.createElement(
2529
- "input",
2530
- {
2531
- className: "rdb-input",
2532
- value: confirmedVal,
2533
- onChange: (e) => setConfirmedVal(e.target.value),
2534
- placeholder: "cth: confirmed, selesai, lunas"
2535
- }
2536
- )), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, 'Nilai "Pending/Proses"'), /* @__PURE__ */ import_react19.default.createElement(
2537
- "input",
2538
- {
2539
- className: "rdb-input",
2540
- value: pendingVal,
2541
- onChange: (e) => setPendingVal(e.target.value),
2542
- placeholder: "cth: pending, proses, menunggu"
2543
- }
2544
- )))), !mappingValid && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-error-banner", style: { marginBottom: 12 } }, "Pilih minimal kolom Tanggal, Status, dan Total untuk generate kode."), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, /* @__PURE__ */ import_react19.default.createElement(
2545
- "button",
2546
- {
2547
- type: "button",
2548
- className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2549
- onClick: () => setStep(1)
2550
- },
2551
- "\u2190 Kembali"
2552
- ), /* @__PURE__ */ import_react19.default.createElement(
2553
- "button",
2554
- {
2555
- type: "button",
2556
- className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2557
- disabled: !mappingValid,
2558
- onClick: handleGenerate
2559
- },
2560
- "\u2728 Generate Kode \u2192"
2561
- ))), step === 3 && generatedCode && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-step-num" }, "\u2705"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Kode Siap Pakai")), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-tip", style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("strong", null, "4 file sudah digenerate."), " Salin masing-masing ke project kamu. Tidak perlu memahami logika di dalamnya \u2014 cukup save dan jalankan."), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issues", style: { marginBottom: 16 } }, [
2562
- ["myDashboardSource.js", "src/datasources/", "File koneksi ke Supabase"],
2563
- ["myDashboardAdapter.js", "src/adapters/", "Transformasi data \u2192 format dashboard"],
2564
- ["myDashboardConfig.js", "src/config/ (atau langsung di Dashboard.jsx)", "Konfigurasi widget"],
2565
- ["Dashboard.jsx", "src/pages/admin/", "Ganti halaman dashboard lama"]
2566
- ].map(([file, path, desc], i) => /* @__PURE__ */ import_react19.default.createElement("div", { key: i, className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", { style: { color: "var(--rdb-blue-500)", fontWeight: 700 } }, i + 1, "."), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, "Simpan ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, file), " ke", " ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, path), " \u2014 ", desc))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", { style: { color: "var(--rdb-blue-500)", fontWeight: 700 } }, "5."), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, "Import CSS di ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, "src/index.css"), ":", " ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, '@import "../node_modules/@rozaqi02/reusable-dashboard/dist/index.css";')))), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 4, marginBottom: 8, flexWrap: "wrap" } }, [
2567
- ["dataSource", "myDashboardSource.js"],
2568
- ["adapter", "myDashboardAdapter.js"],
2569
- ["widgetConfig", "myDashboardConfig.js"],
2570
- ["dashboard", "Dashboard.jsx"]
2571
- ].map(([key, label]) => /* @__PURE__ */ import_react19.default.createElement(
2572
- "button",
2573
- {
2574
- key,
2575
- type: "button",
2576
- onClick: () => setActiveCodeTab(key),
2577
- className: `rdb-btn rdb-btn-sm ${activeCodeTab === key ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2578
- style: { fontFamily: "monospace", fontSize: "0.75rem" }
2579
- },
2580
- label
2581
- ))), /* @__PURE__ */ import_react19.default.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ import_react19.default.createElement(
2582
- "button",
2583
- {
2584
- type: "button",
2585
- onClick: () => copyCode(generatedCode[activeCodeTab], activeCodeTab),
2586
- className: "rdb-btn rdb-btn-sm rdb-btn-secondary",
2587
- style: { position: "absolute", top: 8, right: 8, zIndex: 1 }
2588
- },
2589
- copied === activeCodeTab ? "\u2705 Disalin!" : "\u{1F4CB} Salin"
2590
- ), /* @__PURE__ */ import_react19.default.createElement("pre", { className: "rdb-wizard-code", style: { maxHeight: 320, overflow: "auto" } }, generatedCode[activeCodeTab])), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-tip", style: { marginTop: 12 } }, "Setelah semua file tersimpan dan dev server direstart, wizard tidak akan muncul lagi."), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 12 } }, /* @__PURE__ */ import_react19.default.createElement(
2591
- "button",
2592
- {
2593
- type: "button",
2594
- className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2595
- onClick: () => setStep(2)
2596
- },
2597
- "\u2190 Edit mapping"
2598
- ), /* @__PURE__ */ import_react19.default.createElement(
2599
- "button",
2600
- {
2601
- type: "button",
2602
- className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2603
- onClick: handleDismiss
2604
- },
2605
- "Tutup wizard \u2713"
2606
- ))))))
2607
- );
2403
+ isSelected ? "\u2705 " : "",
2404
+ tbl,
2405
+ isSelected && hasColumns ? ` (${columns[tbl].length} kolom)` : ""
2406
+ );
2407
+ })), /* @__PURE__ */ import_react19.default.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, tables.length > 0 ? "Atau ketik nama tabel lain:" : "Ketik nama tabel kamu:"), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8 } }, /* @__PURE__ */ import_react19.default.createElement(
2408
+ "input",
2409
+ {
2410
+ className: "rdb-input",
2411
+ value: selectedTable,
2412
+ onChange: (e) => setSelectedTable(e.target.value.replace(/[,\s].*/, "")),
2413
+ placeholder: "cth: orders / transaksi / bookings",
2414
+ style: { maxWidth: 280 }
2415
+ }
2416
+ ), /* @__PURE__ */ import_react19.default.createElement(
2417
+ "button",
2418
+ {
2419
+ type: "button",
2420
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2421
+ disabled: !selectedTable || loadingCols,
2422
+ onClick: () => analyzeTable(selectedTable)
2423
+ },
2424
+ loadingCols ? "Menganalisis\u2026" : "Analisis tabel"
2425
+ ))), selectedTable && !loadingCols && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, colNames.length > 0 && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-tip", style: { marginBottom: 8 } }, "\u2705 ", /* @__PURE__ */ import_react19.default.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__ */ import_react19.default.createElement(
2426
+ PreviewTable,
2427
+ {
2428
+ rows: sample,
2429
+ cols: [colDate, colStatus, colTotal, colCustomer].filter(Boolean)
2430
+ }
2431
+ )), colsBlocked && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-alert" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-alert-icon" }, "\u2139\uFE0F"), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { fontWeight: 600 } }, "Kolom tidak bisa terbaca otomatis"), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 4 } }, "Kemungkinan RLS aktif. Kamu tetap bisa lanjut dan ketik nama kolom manual di step berikutnya.")))), /* @__PURE__ */ import_react19.default.createElement("div", { style: { marginBottom: 12, marginTop: 12 } }, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, "Judul dashboard:"), /* @__PURE__ */ import_react19.default.createElement(
2432
+ "input",
2433
+ {
2434
+ className: "rdb-input",
2435
+ value: dashTitle,
2436
+ style: { maxWidth: 280 },
2437
+ onChange: (e) => setDashTitle(e.target.value),
2438
+ placeholder: "cth: Dashboard Laundry / Dashboard Klinik"
2439
+ }
2440
+ )), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, /* @__PURE__ */ import_react19.default.createElement(
2441
+ "button",
2442
+ {
2443
+ type: "button",
2444
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2445
+ onClick: () => setStep(0)
2446
+ },
2447
+ "\u2190 Kembali"
2448
+ ), /* @__PURE__ */ import_react19.default.createElement(
2449
+ "button",
2450
+ {
2451
+ type: "button",
2452
+ className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2453
+ disabled: !selectedTable || loadingCols,
2454
+ onClick: () => setStep(2)
2455
+ },
2456
+ "Lanjut \u2192 Konfirmasi Kolom"
2457
+ ))), step === 2 && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-step-num" }, "3"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Konfirmasi \u2014 Tabel: ", /* @__PURE__ */ import_react19.default.createElement("code", { style: { fontSize: "0.9em" } }, selectedTable))), /* @__PURE__ */ import_react19.default.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__ */ import_react19.default.createElement("div", { className: "rdb-wizard-supabase-reader", style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 6 } }, "Semua kolom di tabel ", selectedTable, ":"), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-table-chips" }, colNames.map((c) => /* @__PURE__ */ import_react19.default.createElement("span", { key: c, className: "rdb-wizard-chip", style: { cursor: "default" } }, c)))), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16 } }, [
2458
+ { label: "Kolom Tanggal *", hint: "Tipe timestamp/date", val: colDate, set: setColDate, ph: "created_at" },
2459
+ { label: "Kolom Status *", hint: "Berisi nilai selesai/pending", val: colStatus, set: setColStatus, ph: "status" },
2460
+ { label: "Kolom Total (uang) *", hint: "Integer Rupiah", val: colTotal, set: setColTotal, ph: "total_amount" },
2461
+ { label: "Kolom Nama Pelanggan", hint: "Opsional", val: colCustomer, set: setColCustomer, ph: "customer_name (opsional)" },
2462
+ { label: "Kolom Nama Item / Layanan", hint: "Opsional", val: colItem, set: setColItem, ph: "product_name (opsional)" }
2463
+ ].map(({ label, hint, val, set, ph }) => /* @__PURE__ */ import_react19.default.createElement("div", { key: label }, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, label), colNames.length > 0 ? /* @__PURE__ */ import_react19.default.createElement("select", { className: "rdb-select", value: val, onChange: (e) => set(e.target.value) }, /* @__PURE__ */ import_react19.default.createElement("option", { value: "" }, "\u2014 pilih \u2014"), colNames.map((c) => /* @__PURE__ */ import_react19.default.createElement("option", { key: c, value: c }, c))) : /* @__PURE__ */ import_react19.default.createElement("input", { className: "rdb-input", value: val, onChange: (e) => set(e.target.value), placeholder: ph }), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 2 } }, hint)))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-flow", style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 4 } }, "Nilai status yang ditemukan di data kamu:"), stVals.length > 0 ? /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { color: "var(--rdb-text-muted)", marginBottom: 8 } }, "Wizard membaca nilai unik dari kolom ", /* @__PURE__ */ import_react19.default.createElement("strong", null, colStatus), ". Klik nilai yang sesuai:"), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 12 } }, stVals.map((v) => /* @__PURE__ */ import_react19.default.createElement("div", { key: v, style: { display: "flex", gap: 4, alignItems: "center" } }, /* @__PURE__ */ import_react19.default.createElement(
2464
+ "button",
2465
+ {
2466
+ type: "button",
2467
+ className: `rdb-btn rdb-btn-sm ${confirmedVal === String(v) ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2468
+ onClick: () => setConfirmedVal(String(v))
2469
+ },
2470
+ confirmedVal === String(v) ? "\u2705 " : "",
2471
+ "Selesai = ",
2472
+ /* @__PURE__ */ import_react19.default.createElement("strong", null, String(v))
2473
+ ), /* @__PURE__ */ import_react19.default.createElement(
2474
+ "button",
2475
+ {
2476
+ type: "button",
2477
+ className: `rdb-btn rdb-btn-sm ${pendingVal === String(v) ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2478
+ onClick: () => setPendingVal(String(v))
2479
+ },
2480
+ pendingVal === String(v) ? "\u2705 " : "",
2481
+ "Proses = ",
2482
+ /* @__PURE__ */ import_react19.default.createElement("strong", null, String(v))
2483
+ ))))) : /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { color: "var(--rdb-text-muted)", marginBottom: 8 } }, "Nilai status tidak terbaca otomatis. Ketik manual:"), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 } }, /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, 'Nilai "Selesai/Confirmed"'), /* @__PURE__ */ import_react19.default.createElement(
2484
+ "input",
2485
+ {
2486
+ className: "rdb-input",
2487
+ value: confirmedVal,
2488
+ onChange: (e) => setConfirmedVal(e.target.value),
2489
+ placeholder: "confirmed"
2490
+ }
2491
+ )), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("label", { className: "rdb-label" }, 'Nilai "Pending/Proses"'), /* @__PURE__ */ import_react19.default.createElement(
2492
+ "input",
2493
+ {
2494
+ className: "rdb-input",
2495
+ value: pendingVal,
2496
+ onChange: (e) => setPendingVal(e.target.value),
2497
+ placeholder: "pending"
2498
+ }
2499
+ )))), sample.length > 0 && colDate && colStatus && colTotal && /* @__PURE__ */ import_react19.default.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { fontWeight: 600, marginBottom: 4 } }, "Preview data nyata sesuai mapping kamu:"), /* @__PURE__ */ import_react19.default.createElement(
2500
+ PreviewTable,
2501
+ {
2502
+ rows: sample,
2503
+ cols: [colDate, colStatus, colTotal, colCustomer, colItem].filter((c) => c && c !== "-")
2504
+ }
2505
+ )), !canGen && /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-error-banner", style: { marginBottom: 12 } }, "Isi Kolom Tanggal, Status, dan Total untuk melanjutkan."), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } }, /* @__PURE__ */ import_react19.default.createElement(
2506
+ "button",
2507
+ {
2508
+ type: "button",
2509
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2510
+ onClick: () => setStep(1)
2511
+ },
2512
+ "\u2190 Kembali"
2513
+ ), /* @__PURE__ */ import_react19.default.createElement(
2514
+ "button",
2515
+ {
2516
+ type: "button",
2517
+ className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2518
+ disabled: !canGen,
2519
+ onClick: handleGenerate
2520
+ },
2521
+ "\u2728 Generate Kode \u2192"
2522
+ ))), step === 3 && generatedCode && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-step-title" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-step-num" }, "\u2705"), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-h3", style: { margin: 0 } }, "Kode Sudah Jadi!")), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-tip", style: { marginBottom: 12 } }, /* @__PURE__ */ import_react19.default.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__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issues", style: { marginBottom: 12 } }, Object.entries(TAB_INFO).map(([key, info], i) => /* @__PURE__ */ import_react19.default.createElement("div", { key, className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", { style: { color: "var(--rdb-blue-500)", fontWeight: 700, minWidth: 18 } }, i + 1, "."), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, "Salin ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, info.label), " \u2192 simpan ke ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, info.path)))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", { style: { color: "var(--rdb-blue-500)", fontWeight: 700, minWidth: 18 } }, "5."), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, "Di ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, "src/index.css"), ", tambahkan:", " ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, '@import "../node_modules/@rozaqi02/reusable-dashboard/dist/index.css";'))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-issue-item" }, /* @__PURE__ */ import_react19.default.createElement("span", { style: { color: "var(--rdb-blue-500)", fontWeight: 700, minWidth: 18 } }, "6."), /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-body" }, "Restart dev server (", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, "npm start"), ") \u2192 buka halaman dashboard \u2192 selesai!"))), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 4, flexWrap: "wrap", marginBottom: 4 } }, Object.entries(TAB_INFO).map(([key, info]) => /* @__PURE__ */ import_react19.default.createElement(
2523
+ "button",
2524
+ {
2525
+ key,
2526
+ type: "button",
2527
+ onClick: () => setActiveTab(key),
2528
+ className: `rdb-btn rdb-btn-sm ${activeTab === key ? "rdb-btn-primary" : "rdb-btn-secondary"}`,
2529
+ style: { fontFamily: "monospace", fontSize: "0.73rem" }
2530
+ },
2531
+ info.label
2532
+ ))), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { color: "var(--rdb-text-muted)", marginBottom: 6 } }, "\u{1F4C1} Simpan ke: ", /* @__PURE__ */ import_react19.default.createElement("code", { className: "rdb-wizard-code-inline" }, (_b = TAB_INFO[activeTab]) == null ? void 0 : _b.path)), /* @__PURE__ */ import_react19.default.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ import_react19.default.createElement(
2533
+ "button",
2534
+ {
2535
+ type: "button",
2536
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2537
+ style: { position: "absolute", top: 8, right: 8, zIndex: 1 },
2538
+ onClick: () => copyCode(generatedCode[activeTab], activeTab)
2539
+ },
2540
+ copied === activeTab ? "\u2705 Tersalin!" : "\u{1F4CB} Salin kode"
2541
+ ), /* @__PURE__ */ import_react19.default.createElement("pre", { className: "rdb-wizard-code", style: { maxHeight: 280, overflow: "auto" } }, generatedCode[activeTab])), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-wizard-alert", style: { marginTop: 10, marginBottom: 4 } }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "rdb-wizard-alert-icon" }, "\u26A0\uFE0F"), /* @__PURE__ */ import_react19.default.createElement("div", null, /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-body", style: { fontWeight: 600 } }, "Data tidak muncul di dashboard?"), /* @__PURE__ */ import_react19.default.createElement("div", { className: "rdb-caption", style: { marginTop: 4 } }, "Jalankan SQL ini di Supabase SQL Editor:"), /* @__PURE__ */ import_react19.default.createElement("div", { style: { position: "relative", marginTop: 6 } }, /* @__PURE__ */ import_react19.default.createElement(
2542
+ "button",
2543
+ {
2544
+ type: "button",
2545
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2546
+ style: { position: "absolute", top: 4, right: 4 },
2547
+ onClick: () => copyCode(`ALTER TABLE public.${selectedTable} DISABLE ROW LEVEL SECURITY;`, "rlsquick")
2548
+ },
2549
+ copied === "rlsquick" ? "\u2705" : "\u{1F4CB}"
2550
+ ), /* @__PURE__ */ import_react19.default.createElement("pre", { className: "rdb-wizard-code", style: { fontSize: "0.72rem" } }, `ALTER TABLE public.${selectedTable} DISABLE ROW LEVEL SECURITY;`)))), /* @__PURE__ */ import_react19.default.createElement("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 10 } }, /* @__PURE__ */ import_react19.default.createElement(
2551
+ "button",
2552
+ {
2553
+ type: "button",
2554
+ className: "rdb-btn rdb-btn-secondary rdb-btn-sm",
2555
+ onClick: () => setStep(2)
2556
+ },
2557
+ "\u2190 Edit mapping"
2558
+ ), /* @__PURE__ */ import_react19.default.createElement(
2559
+ "button",
2560
+ {
2561
+ type: "button",
2562
+ className: "rdb-btn rdb-btn-primary rdb-btn-sm",
2563
+ onClick: handleDismiss
2564
+ },
2565
+ "Tutup wizard \u2713"
2566
+ )))))));
2608
2567
  }
2609
2568
  SetupWizard.propTypes = {
2610
2569
  issues: import_prop_types17.default.arrayOf(import_prop_types17.default.string),