@universe-platform/ui 0.1.0-beta.49 → 0.1.0-beta.50

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/globals.css CHANGED
@@ -138,6 +138,7 @@
138
138
  --font-weight-bold: 700;
139
139
  --tracking-wider: 0.05em;
140
140
  --leading-tight: 1.25;
141
+ --leading-snug: 1.375;
141
142
  --leading-normal: 1.5;
142
143
  --leading-relaxed: 1.625;
143
144
  --radius-md: 0.375rem;
@@ -1887,6 +1888,10 @@
1887
1888
  --tw-leading: var(--leading-relaxed);
1888
1889
  line-height: var(--leading-relaxed);
1889
1890
  }
1891
+ .leading-snug {
1892
+ --tw-leading: var(--leading-snug);
1893
+ line-height: var(--leading-snug);
1894
+ }
1890
1895
  .leading-tight {
1891
1896
  --tw-leading: var(--leading-tight);
1892
1897
  line-height: var(--leading-tight);
package/dist/index.js CHANGED
@@ -6390,6 +6390,25 @@ function TableBlockClient({
6390
6390
  }) {
6391
6391
  const { showError, addToast } = useToast();
6392
6392
  const [isExporting, setIsExporting] = (0, import_react18.useState)(false);
6393
+ const [isExportMenuOpen, setIsExportMenuOpen] = (0, import_react18.useState)(false);
6394
+ const exportMenuRef = (0, import_react18.useRef)(null);
6395
+ (0, import_react18.useEffect)(() => {
6396
+ if (!isExportMenuOpen) return;
6397
+ const handleClickOutside = (e) => {
6398
+ if (exportMenuRef.current && !exportMenuRef.current.contains(e.target)) {
6399
+ setIsExportMenuOpen(false);
6400
+ }
6401
+ };
6402
+ const handleEscape = (e) => {
6403
+ if (e.key === "Escape") setIsExportMenuOpen(false);
6404
+ };
6405
+ document.addEventListener("mousedown", handleClickOutside);
6406
+ document.addEventListener("keydown", handleEscape);
6407
+ return () => {
6408
+ document.removeEventListener("mousedown", handleClickOutside);
6409
+ document.removeEventListener("keydown", handleEscape);
6410
+ };
6411
+ }, [isExportMenuOpen]);
6393
6412
  const resolvedRole = role ? getRolePermissions(role) : void 0;
6394
6413
  const effectivePermissions = permissions || (resolvedRole == null ? void 0 : resolvedRole.permissions) || {
6395
6414
  canCreate: !readOnly,
@@ -7630,19 +7649,30 @@ function TableBlockClient({
7630
7649
  (v) => Array.isArray(v) ? v.length > 0 : v && typeof v === "object" && "values" in v ? v.values.length > 0 : typeof v === "string" && Boolean(v.trim())
7631
7650
  );
7632
7651
  let exportButtonText = "\u05D9\u05D9\u05E6\u05D5\u05D0 \u05E9\u05DC \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD";
7652
+ let exportButtonHint = "\u05DB\u05DC \u05D4\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D1\u05D8\u05D1\u05DC\u05D4, \u05DB\u05D5\u05DC\u05DC \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD \u05D4\u05E7\u05D9\u05D9\u05DE\u05D9\u05DD";
7633
7653
  let ExportButtonIcon = import_lucide_react9.Download;
7634
7654
  if (hasSelectedRows) {
7635
7655
  exportButtonText = `\u05D9\u05D9\u05E6\u05D0 ${selectedIds.size} \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA`;
7656
+ exportButtonHint = "\u05E8\u05E7 \u05D4\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05E9\u05E1\u05D9\u05DE\u05E0\u05EA \u05D1\u05D8\u05D1\u05DC\u05D4";
7636
7657
  ExportButtonIcon = import_lucide_react9.Download;
7637
7658
  } else if (hasActiveSearchOrFilter) {
7638
7659
  exportButtonText = "\u05D9\u05D9\u05E6\u05D0 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E1\u05D9\u05E0\u05D5\u05DF";
7660
+ exportButtonHint = "\u05E8\u05E7 \u05D4\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05E9\u05DE\u05EA\u05D0\u05D9\u05DE\u05D5\u05EA \u05DC\u05D7\u05D9\u05E4\u05D5\u05E9 / \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D4\u05E0\u05D5\u05DB\u05D7\u05D9";
7639
7661
  ExportButtonIcon = import_lucide_react9.Filter;
7640
7662
  }
7641
- const handleExportCsv = async () => {
7663
+ const handleExportCsv = async (mode = "data") => {
7642
7664
  if (isExporting) return;
7665
+ setIsExportMenuOpen(false);
7666
+ const isTemplate = mode === "template";
7643
7667
  const systemFields = ["_id", "id", "createdAt", "updatedAt", "createdBy", "updatedBy", "orgId", "tableId"];
7644
7668
  const userHiddenColumns = normalizedColumns.filter((c) => c.isHidden && !systemFields.includes(c.name)).map((c) => c.name);
7645
- if (userHiddenColumns.length > 0) {
7669
+ if (isTemplate) {
7670
+ addToast({
7671
+ message: "\u05DE\u05DB\u05D9\u05DF \u05EA\u05D1\u05E0\u05D9\u05EA \u05E8\u05D9\u05E7\u05D4 \u05DC\u05D9\u05D9\u05D1\u05D5\u05D0...",
7672
+ type: "info",
7673
+ duration: 3e3
7674
+ });
7675
+ } else if (userHiddenColumns.length > 0) {
7646
7676
  addToast({
7647
7677
  message: "\u05DE\u05D9\u05D9\u05E6\u05D0 \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD... \u05E9\u05D9\u05DD \u05DC\u05D1: \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05DE\u05D5\u05E1\u05EA\u05E8\u05D5\u05EA \u05DC\u05D0 \u05D9\u05D9\u05DB\u05DC\u05DC\u05D5 \u05D1\u05E7\u05D5\u05D1\u05E5",
7648
7678
  type: "info",
@@ -7662,9 +7692,14 @@ function TableBlockClient({
7662
7692
  showError("\u05DE\u05D6\u05D4\u05D4 \u05D4\u05D8\u05D1\u05DC\u05D4 \u05D7\u05E1\u05E8 (tableId)", "\u05E9\u05D2\u05D9\u05D0\u05EA \u05D9\u05D9\u05E6\u05D5\u05D0");
7663
7693
  return;
7664
7694
  }
7665
- const payload = {
7695
+ const payload = isTemplate ? {
7666
7696
  tableId: targetTableId,
7667
7697
  orgId,
7698
+ mode: "template"
7699
+ } : {
7700
+ tableId: targetTableId,
7701
+ orgId,
7702
+ mode: "data",
7668
7703
  search: searchQuery.trim() || void 0,
7669
7704
  columnFilters: Object.keys(columnFilters).length > 0 ? columnFilters : void 0,
7670
7705
  selectedRowIds: hasSelectedRows ? Array.from(selectedIds) : [],
@@ -7688,7 +7723,7 @@ function TableBlockClient({
7688
7723
  throw new Error("No export data received from server");
7689
7724
  }
7690
7725
  let blob;
7691
- const defaultFileName = `${((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name) || "table").trim()}_export.xlsx`;
7726
+ const defaultFileName = `${((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name) || "table").trim()}_${isTemplate ? "template" : "export"}.xlsx`;
7692
7727
  let fileName = (result == null ? void 0 : result.fileName) || defaultFileName;
7693
7728
  if (base64Data) {
7694
7729
  const byteCharacters = atob(base64Data);
@@ -7711,7 +7746,7 @@ function TableBlockClient({
7711
7746
  link.click();
7712
7747
  document.body.removeChild(link);
7713
7748
  URL.revokeObjectURL(url);
7714
- if (result == null ? void 0 : result.isTruncated) {
7749
+ if (!isTemplate && (result == null ? void 0 : result.isTruncated)) {
7715
7750
  addToast({
7716
7751
  message: "\u05D9\u05D5\u05E6\u05D0\u05D5 10,000 \u05D4\u05E9\u05D5\u05E8\u05D5\u05EA \u05D4\u05E8\u05D0\u05E9\u05D5\u05E0\u05D5\u05EA. \u05DE\u05D5\u05DE\u05DC\u05E5 \u05DC\u05E1\u05E0\u05DF \u05D0\u05EA \u05D4\u05D8\u05D1\u05DC\u05D4 \u05DB\u05D3\u05D9 \u05DC\u05E6\u05DE\u05E6\u05DD \u05D0\u05EA \u05D4\u05DB\u05DE\u05D5\u05EA",
7717
7752
  type: "warning",
@@ -7720,7 +7755,7 @@ function TableBlockClient({
7720
7755
  }
7721
7756
  } catch (err) {
7722
7757
  console.error("[TableBlock] Export to CSV failed:", err);
7723
- showError((err == null ? void 0 : err.message) || String(err), "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D9\u05D9\u05E6\u05D5\u05D0 \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD");
7758
+ showError((err == null ? void 0 : err.message) || String(err), isTemplate ? "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D9\u05D9\u05E6\u05D5\u05D0 \u05D4\u05EA\u05D1\u05E0\u05D9\u05EA" : "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D9\u05D9\u05E6\u05D5\u05D0 \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD");
7724
7759
  } finally {
7725
7760
  setIsExporting(false);
7726
7761
  }
@@ -7899,20 +7934,69 @@ function TableBlockClient({
7899
7934
  }) })
7900
7935
  ] })
7901
7936
  ] }),
7902
- role === "OWNER" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7903
- "button",
7904
- {
7905
- onClick: handleExportCsv,
7906
- disabled: isLoading || isUpdatingActive || isExporting,
7907
- className: `h-9 px-3 font-semibold rounded-lg shadow-2xs flex items-center gap-1.5 text-xs transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer border ${hasSelectedRows ? "bg-blue-50 hover:bg-blue-100 border-blue-200 text-blue-700" : hasActiveSearchOrFilter ? "bg-amber-50 hover:bg-amber-100 border-amber-200 text-amber-800" : "bg-white hover:bg-slate-100 border-slate-300 text-slate-700"}`,
7908
- title: exportButtonText,
7909
- "data-testid": "export-csv-button",
7910
- children: [
7911
- isExporting ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react9.Loader2, { className: "w-4 h-4 shrink-0 animate-spin text-slate-500" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ExportButtonIcon, { className: "w-4 h-4 shrink-0" }),
7912
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: isExporting ? "\u05DE\u05D9\u05D9\u05E6\u05D0..." : exportButtonText })
7913
- ]
7914
- }
7915
- ),
7937
+ role === "OWNER" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "relative", ref: exportMenuRef, children: [
7938
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7939
+ "button",
7940
+ {
7941
+ type: "button",
7942
+ onClick: () => setIsExportMenuOpen((prev) => !prev),
7943
+ disabled: isLoading || isUpdatingActive || isExporting,
7944
+ "aria-haspopup": "menu",
7945
+ "aria-expanded": isExportMenuOpen,
7946
+ className: `h-9 px-3 font-semibold rounded-lg shadow-2xs flex items-center gap-1.5 text-xs transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer border ${hasSelectedRows ? "bg-blue-50 hover:bg-blue-100 border-blue-200 text-blue-700" : hasActiveSearchOrFilter ? "bg-amber-50 hover:bg-amber-100 border-amber-200 text-amber-800" : "bg-white hover:bg-slate-100 border-slate-300 text-slate-700"}`,
7947
+ title: "\u05D9\u05D9\u05E6\u05D5\u05D0",
7948
+ "data-testid": "export-csv-button",
7949
+ children: [
7950
+ isExporting ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react9.Loader2, { className: "w-4 h-4 shrink-0 animate-spin text-slate-500" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react9.Download, { className: "w-4 h-4 shrink-0" }),
7951
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: isExporting ? "\u05DE\u05D9\u05D9\u05E6\u05D0..." : "\u05D9\u05D9\u05E6\u05D5\u05D0" }),
7952
+ !isExporting && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react9.ChevronDown, { className: `w-3.5 h-3.5 shrink-0 opacity-70 transition-transform ${isExportMenuOpen ? "rotate-180" : ""}` })
7953
+ ]
7954
+ }
7955
+ ),
7956
+ isExportMenuOpen && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7957
+ "div",
7958
+ {
7959
+ role: "menu",
7960
+ className: "absolute right-0 top-full mt-1.5 w-72 bg-white border border-slate-200 rounded-xl shadow-xl z-50 p-1.5 dir-rtl animate-in fade-in slide-in-from-top-2 duration-150",
7961
+ children: [
7962
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7963
+ "button",
7964
+ {
7965
+ type: "button",
7966
+ role: "menuitem",
7967
+ onClick: () => handleExportCsv("data"),
7968
+ className: "w-full flex items-start gap-2.5 px-2.5 py-2 rounded-lg text-right hover:bg-slate-100 active:bg-slate-200 transition-colors cursor-pointer",
7969
+ "data-testid": "export-data-option",
7970
+ children: [
7971
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ExportButtonIcon, { className: "w-4 h-4 mt-0.5 shrink-0 text-slate-600" }),
7972
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "flex flex-col min-w-0", children: [
7973
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-xs font-semibold text-slate-800", children: exportButtonText }),
7974
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] font-normal text-slate-500 leading-snug", children: exportButtonHint })
7975
+ ] })
7976
+ ]
7977
+ }
7978
+ ),
7979
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7980
+ "button",
7981
+ {
7982
+ type: "button",
7983
+ role: "menuitem",
7984
+ onClick: () => handleExportCsv("template"),
7985
+ className: "w-full flex items-start gap-2.5 px-2.5 py-2 rounded-lg text-right hover:bg-slate-100 active:bg-slate-200 transition-colors cursor-pointer",
7986
+ "data-testid": "export-template-option",
7987
+ children: [
7988
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react9.FileSpreadsheet, { className: "w-4 h-4 mt-0.5 shrink-0 text-slate-600" }),
7989
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "flex flex-col min-w-0", children: [
7990
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-xs font-semibold text-slate-800", children: "\u05D9\u05D9\u05E6\u05D5\u05D0 \u05EA\u05D1\u05E0\u05D9\u05EA \u05E8\u05D9\u05E7\u05D4 \u05DC\u05D9\u05D9\u05D1\u05D5\u05D0" }),
7991
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[11px] font-normal text-slate-500 leading-snug", children: "\u05DB\u05D5\u05EA\u05E8\u05D5\u05EA \u05D4\u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D1\u05DC\u05D1\u05D3, \u05D1\u05DC\u05D9 \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD, \u05D5\u05D2\u05D9\u05DC\u05D9\u05D5\u05DF \u05E0\u05D5\u05E1\u05E3 \u05E9\u05DE\u05E1\u05D1\u05D9\u05E8 \u05D0\u05EA \u05E1\u05D5\u05D2 \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD \u05D1\u05DB\u05DC \u05E2\u05DE\u05D5\u05D3\u05D4" })
7992
+ ] })
7993
+ ]
7994
+ }
7995
+ )
7996
+ ]
7997
+ }
7998
+ )
7999
+ ] }),
7916
8000
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-5 w-px bg-slate-300 mx-1 hidden sm:block" }),
7917
8001
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "relative flex-1 sm:w-64 max-w-xs", children: [
7918
8002
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
package/dist/index.mjs CHANGED
@@ -5694,7 +5694,7 @@ function SubTableViewer({ rowId, column, value, onChange, context, readOnly }) {
5694
5694
  }
5695
5695
 
5696
5696
  // src/components/TableBlock/TableBlockClient.tsx
5697
- import { X as X8, Copy as Copy2, RotateCcw as RotateCcw2, Download, Filter, Loader2 as Loader23, ExternalLink as ExternalLink2 } from "lucide-react";
5697
+ import { X as X8, Copy as Copy2, RotateCcw as RotateCcw2, Download, Filter, Loader2 as Loader23, ExternalLink as ExternalLink2, ChevronDown as ChevronDown2, FileSpreadsheet } from "lucide-react";
5698
5698
 
5699
5699
  // src/components/TableBlock/useRelationCopy.ts
5700
5700
  import { useCallback as useCallback10, useRef as useRef9 } from "react";
@@ -6239,6 +6239,25 @@ function TableBlockClient({
6239
6239
  }) {
6240
6240
  const { showError, addToast } = useToast();
6241
6241
  const [isExporting, setIsExporting] = useState15(false);
6242
+ const [isExportMenuOpen, setIsExportMenuOpen] = useState15(false);
6243
+ const exportMenuRef = useRef13(null);
6244
+ useEffect13(() => {
6245
+ if (!isExportMenuOpen) return;
6246
+ const handleClickOutside = (e) => {
6247
+ if (exportMenuRef.current && !exportMenuRef.current.contains(e.target)) {
6248
+ setIsExportMenuOpen(false);
6249
+ }
6250
+ };
6251
+ const handleEscape = (e) => {
6252
+ if (e.key === "Escape") setIsExportMenuOpen(false);
6253
+ };
6254
+ document.addEventListener("mousedown", handleClickOutside);
6255
+ document.addEventListener("keydown", handleEscape);
6256
+ return () => {
6257
+ document.removeEventListener("mousedown", handleClickOutside);
6258
+ document.removeEventListener("keydown", handleEscape);
6259
+ };
6260
+ }, [isExportMenuOpen]);
6242
6261
  const resolvedRole = role ? getRolePermissions(role) : void 0;
6243
6262
  const effectivePermissions = permissions || (resolvedRole == null ? void 0 : resolvedRole.permissions) || {
6244
6263
  canCreate: !readOnly,
@@ -7479,19 +7498,30 @@ function TableBlockClient({
7479
7498
  (v) => Array.isArray(v) ? v.length > 0 : v && typeof v === "object" && "values" in v ? v.values.length > 0 : typeof v === "string" && Boolean(v.trim())
7480
7499
  );
7481
7500
  let exportButtonText = "\u05D9\u05D9\u05E6\u05D5\u05D0 \u05E9\u05DC \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD";
7501
+ let exportButtonHint = "\u05DB\u05DC \u05D4\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D1\u05D8\u05D1\u05DC\u05D4, \u05DB\u05D5\u05DC\u05DC \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD \u05D4\u05E7\u05D9\u05D9\u05DE\u05D9\u05DD";
7482
7502
  let ExportButtonIcon = Download;
7483
7503
  if (hasSelectedRows) {
7484
7504
  exportButtonText = `\u05D9\u05D9\u05E6\u05D0 ${selectedIds.size} \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA`;
7505
+ exportButtonHint = "\u05E8\u05E7 \u05D4\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05E9\u05E1\u05D9\u05DE\u05E0\u05EA \u05D1\u05D8\u05D1\u05DC\u05D4";
7485
7506
  ExportButtonIcon = Download;
7486
7507
  } else if (hasActiveSearchOrFilter) {
7487
7508
  exportButtonText = "\u05D9\u05D9\u05E6\u05D0 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E1\u05D9\u05E0\u05D5\u05DF";
7509
+ exportButtonHint = "\u05E8\u05E7 \u05D4\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05E9\u05DE\u05EA\u05D0\u05D9\u05DE\u05D5\u05EA \u05DC\u05D7\u05D9\u05E4\u05D5\u05E9 / \u05DC\u05E1\u05D9\u05E0\u05D5\u05DF \u05D4\u05E0\u05D5\u05DB\u05D7\u05D9";
7488
7510
  ExportButtonIcon = Filter;
7489
7511
  }
7490
- const handleExportCsv = async () => {
7512
+ const handleExportCsv = async (mode = "data") => {
7491
7513
  if (isExporting) return;
7514
+ setIsExportMenuOpen(false);
7515
+ const isTemplate = mode === "template";
7492
7516
  const systemFields = ["_id", "id", "createdAt", "updatedAt", "createdBy", "updatedBy", "orgId", "tableId"];
7493
7517
  const userHiddenColumns = normalizedColumns.filter((c) => c.isHidden && !systemFields.includes(c.name)).map((c) => c.name);
7494
- if (userHiddenColumns.length > 0) {
7518
+ if (isTemplate) {
7519
+ addToast({
7520
+ message: "\u05DE\u05DB\u05D9\u05DF \u05EA\u05D1\u05E0\u05D9\u05EA \u05E8\u05D9\u05E7\u05D4 \u05DC\u05D9\u05D9\u05D1\u05D5\u05D0...",
7521
+ type: "info",
7522
+ duration: 3e3
7523
+ });
7524
+ } else if (userHiddenColumns.length > 0) {
7495
7525
  addToast({
7496
7526
  message: "\u05DE\u05D9\u05D9\u05E6\u05D0 \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD... \u05E9\u05D9\u05DD \u05DC\u05D1: \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05DE\u05D5\u05E1\u05EA\u05E8\u05D5\u05EA \u05DC\u05D0 \u05D9\u05D9\u05DB\u05DC\u05DC\u05D5 \u05D1\u05E7\u05D5\u05D1\u05E5",
7497
7527
  type: "info",
@@ -7511,9 +7541,14 @@ function TableBlockClient({
7511
7541
  showError("\u05DE\u05D6\u05D4\u05D4 \u05D4\u05D8\u05D1\u05DC\u05D4 \u05D7\u05E1\u05E8 (tableId)", "\u05E9\u05D2\u05D9\u05D0\u05EA \u05D9\u05D9\u05E6\u05D5\u05D0");
7512
7542
  return;
7513
7543
  }
7514
- const payload = {
7544
+ const payload = isTemplate ? {
7545
+ tableId: targetTableId,
7546
+ orgId,
7547
+ mode: "template"
7548
+ } : {
7515
7549
  tableId: targetTableId,
7516
7550
  orgId,
7551
+ mode: "data",
7517
7552
  search: searchQuery.trim() || void 0,
7518
7553
  columnFilters: Object.keys(columnFilters).length > 0 ? columnFilters : void 0,
7519
7554
  selectedRowIds: hasSelectedRows ? Array.from(selectedIds) : [],
@@ -7537,7 +7572,7 @@ function TableBlockClient({
7537
7572
  throw new Error("No export data received from server");
7538
7573
  }
7539
7574
  let blob;
7540
- const defaultFileName = `${((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name) || "table").trim()}_export.xlsx`;
7575
+ const defaultFileName = `${((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name) || "table").trim()}_${isTemplate ? "template" : "export"}.xlsx`;
7541
7576
  let fileName = (result == null ? void 0 : result.fileName) || defaultFileName;
7542
7577
  if (base64Data) {
7543
7578
  const byteCharacters = atob(base64Data);
@@ -7560,7 +7595,7 @@ function TableBlockClient({
7560
7595
  link.click();
7561
7596
  document.body.removeChild(link);
7562
7597
  URL.revokeObjectURL(url);
7563
- if (result == null ? void 0 : result.isTruncated) {
7598
+ if (!isTemplate && (result == null ? void 0 : result.isTruncated)) {
7564
7599
  addToast({
7565
7600
  message: "\u05D9\u05D5\u05E6\u05D0\u05D5 10,000 \u05D4\u05E9\u05D5\u05E8\u05D5\u05EA \u05D4\u05E8\u05D0\u05E9\u05D5\u05E0\u05D5\u05EA. \u05DE\u05D5\u05DE\u05DC\u05E5 \u05DC\u05E1\u05E0\u05DF \u05D0\u05EA \u05D4\u05D8\u05D1\u05DC\u05D4 \u05DB\u05D3\u05D9 \u05DC\u05E6\u05DE\u05E6\u05DD \u05D0\u05EA \u05D4\u05DB\u05DE\u05D5\u05EA",
7566
7601
  type: "warning",
@@ -7569,7 +7604,7 @@ function TableBlockClient({
7569
7604
  }
7570
7605
  } catch (err) {
7571
7606
  console.error("[TableBlock] Export to CSV failed:", err);
7572
- showError((err == null ? void 0 : err.message) || String(err), "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D9\u05D9\u05E6\u05D5\u05D0 \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD");
7607
+ showError((err == null ? void 0 : err.message) || String(err), isTemplate ? "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D9\u05D9\u05E6\u05D5\u05D0 \u05D4\u05EA\u05D1\u05E0\u05D9\u05EA" : "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D9\u05D9\u05E6\u05D5\u05D0 \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD");
7573
7608
  } finally {
7574
7609
  setIsExporting(false);
7575
7610
  }
@@ -7748,20 +7783,69 @@ function TableBlockClient({
7748
7783
  }) })
7749
7784
  ] })
7750
7785
  ] }),
7751
- role === "OWNER" && /* @__PURE__ */ jsxs13(
7752
- "button",
7753
- {
7754
- onClick: handleExportCsv,
7755
- disabled: isLoading || isUpdatingActive || isExporting,
7756
- className: `h-9 px-3 font-semibold rounded-lg shadow-2xs flex items-center gap-1.5 text-xs transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer border ${hasSelectedRows ? "bg-blue-50 hover:bg-blue-100 border-blue-200 text-blue-700" : hasActiveSearchOrFilter ? "bg-amber-50 hover:bg-amber-100 border-amber-200 text-amber-800" : "bg-white hover:bg-slate-100 border-slate-300 text-slate-700"}`,
7757
- title: exportButtonText,
7758
- "data-testid": "export-csv-button",
7759
- children: [
7760
- isExporting ? /* @__PURE__ */ jsx15(Loader23, { className: "w-4 h-4 shrink-0 animate-spin text-slate-500" }) : /* @__PURE__ */ jsx15(ExportButtonIcon, { className: "w-4 h-4 shrink-0" }),
7761
- /* @__PURE__ */ jsx15("span", { children: isExporting ? "\u05DE\u05D9\u05D9\u05E6\u05D0..." : exportButtonText })
7762
- ]
7763
- }
7764
- ),
7786
+ role === "OWNER" && /* @__PURE__ */ jsxs13("div", { className: "relative", ref: exportMenuRef, children: [
7787
+ /* @__PURE__ */ jsxs13(
7788
+ "button",
7789
+ {
7790
+ type: "button",
7791
+ onClick: () => setIsExportMenuOpen((prev) => !prev),
7792
+ disabled: isLoading || isUpdatingActive || isExporting,
7793
+ "aria-haspopup": "menu",
7794
+ "aria-expanded": isExportMenuOpen,
7795
+ className: `h-9 px-3 font-semibold rounded-lg shadow-2xs flex items-center gap-1.5 text-xs transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer border ${hasSelectedRows ? "bg-blue-50 hover:bg-blue-100 border-blue-200 text-blue-700" : hasActiveSearchOrFilter ? "bg-amber-50 hover:bg-amber-100 border-amber-200 text-amber-800" : "bg-white hover:bg-slate-100 border-slate-300 text-slate-700"}`,
7796
+ title: "\u05D9\u05D9\u05E6\u05D5\u05D0",
7797
+ "data-testid": "export-csv-button",
7798
+ children: [
7799
+ isExporting ? /* @__PURE__ */ jsx15(Loader23, { className: "w-4 h-4 shrink-0 animate-spin text-slate-500" }) : /* @__PURE__ */ jsx15(Download, { className: "w-4 h-4 shrink-0" }),
7800
+ /* @__PURE__ */ jsx15("span", { children: isExporting ? "\u05DE\u05D9\u05D9\u05E6\u05D0..." : "\u05D9\u05D9\u05E6\u05D5\u05D0" }),
7801
+ !isExporting && /* @__PURE__ */ jsx15(ChevronDown2, { className: `w-3.5 h-3.5 shrink-0 opacity-70 transition-transform ${isExportMenuOpen ? "rotate-180" : ""}` })
7802
+ ]
7803
+ }
7804
+ ),
7805
+ isExportMenuOpen && /* @__PURE__ */ jsxs13(
7806
+ "div",
7807
+ {
7808
+ role: "menu",
7809
+ className: "absolute right-0 top-full mt-1.5 w-72 bg-white border border-slate-200 rounded-xl shadow-xl z-50 p-1.5 dir-rtl animate-in fade-in slide-in-from-top-2 duration-150",
7810
+ children: [
7811
+ /* @__PURE__ */ jsxs13(
7812
+ "button",
7813
+ {
7814
+ type: "button",
7815
+ role: "menuitem",
7816
+ onClick: () => handleExportCsv("data"),
7817
+ className: "w-full flex items-start gap-2.5 px-2.5 py-2 rounded-lg text-right hover:bg-slate-100 active:bg-slate-200 transition-colors cursor-pointer",
7818
+ "data-testid": "export-data-option",
7819
+ children: [
7820
+ /* @__PURE__ */ jsx15(ExportButtonIcon, { className: "w-4 h-4 mt-0.5 shrink-0 text-slate-600" }),
7821
+ /* @__PURE__ */ jsxs13("span", { className: "flex flex-col min-w-0", children: [
7822
+ /* @__PURE__ */ jsx15("span", { className: "text-xs font-semibold text-slate-800", children: exportButtonText }),
7823
+ /* @__PURE__ */ jsx15("span", { className: "text-[11px] font-normal text-slate-500 leading-snug", children: exportButtonHint })
7824
+ ] })
7825
+ ]
7826
+ }
7827
+ ),
7828
+ /* @__PURE__ */ jsxs13(
7829
+ "button",
7830
+ {
7831
+ type: "button",
7832
+ role: "menuitem",
7833
+ onClick: () => handleExportCsv("template"),
7834
+ className: "w-full flex items-start gap-2.5 px-2.5 py-2 rounded-lg text-right hover:bg-slate-100 active:bg-slate-200 transition-colors cursor-pointer",
7835
+ "data-testid": "export-template-option",
7836
+ children: [
7837
+ /* @__PURE__ */ jsx15(FileSpreadsheet, { className: "w-4 h-4 mt-0.5 shrink-0 text-slate-600" }),
7838
+ /* @__PURE__ */ jsxs13("span", { className: "flex flex-col min-w-0", children: [
7839
+ /* @__PURE__ */ jsx15("span", { className: "text-xs font-semibold text-slate-800", children: "\u05D9\u05D9\u05E6\u05D5\u05D0 \u05EA\u05D1\u05E0\u05D9\u05EA \u05E8\u05D9\u05E7\u05D4 \u05DC\u05D9\u05D9\u05D1\u05D5\u05D0" }),
7840
+ /* @__PURE__ */ jsx15("span", { className: "text-[11px] font-normal text-slate-500 leading-snug", children: "\u05DB\u05D5\u05EA\u05E8\u05D5\u05EA \u05D4\u05E2\u05DE\u05D5\u05D3\u05D5\u05EA \u05D1\u05DC\u05D1\u05D3, \u05D1\u05DC\u05D9 \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD, \u05D5\u05D2\u05D9\u05DC\u05D9\u05D5\u05DF \u05E0\u05D5\u05E1\u05E3 \u05E9\u05DE\u05E1\u05D1\u05D9\u05E8 \u05D0\u05EA \u05E1\u05D5\u05D2 \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD \u05D1\u05DB\u05DC \u05E2\u05DE\u05D5\u05D3\u05D4" })
7841
+ ] })
7842
+ ]
7843
+ }
7844
+ )
7845
+ ]
7846
+ }
7847
+ )
7848
+ ] }),
7765
7849
  /* @__PURE__ */ jsx15("div", { className: "h-5 w-px bg-slate-300 mx-1 hidden sm:block" }),
7766
7850
  /* @__PURE__ */ jsxs13("div", { className: "relative flex-1 sm:w-64 max-w-xs", children: [
7767
7851
  /* @__PURE__ */ jsx15(
@@ -9658,7 +9742,7 @@ import {
9658
9742
  Phone,
9659
9743
  Copy as Copy3,
9660
9744
  MessageSquare,
9661
- ChevronDown as ChevronDown2
9745
+ ChevronDown as ChevronDown3
9662
9746
  } from "lucide-react";
9663
9747
  import EmojiPicker, { Theme } from "emoji-picker-react";
9664
9748
 
@@ -10520,7 +10604,7 @@ function CreateTemplateModal({
10520
10604
  /* @__PURE__ */ jsx20(Plus, { className: "w-3.5 h-3.5" }),
10521
10605
  /* @__PURE__ */ jsx20("span", { children: "\u05D4\u05D5\u05E1\u05E3 \u05DB\u05E4\u05EA\u05D5\u05E8" }),
10522
10606
  /* @__PURE__ */ jsx20(
10523
- ChevronDown2,
10607
+ ChevronDown3,
10524
10608
  {
10525
10609
  className: `w-3.5 h-3.5 transition-transform ${isAddButtonOpen ? "rotate-180" : ""}`
10526
10610
  }
@@ -13553,7 +13637,7 @@ import {
13553
13637
  Trash2 as Trash23,
13554
13638
  Plus as Plus2,
13555
13639
  Loader2 as Loader25,
13556
- ChevronDown as ChevronDown3,
13640
+ ChevronDown as ChevronDown4,
13557
13641
  Image as ImageIcon2,
13558
13642
  ExternalLink as ExternalLink4,
13559
13643
  Video as Video2,
@@ -13655,7 +13739,7 @@ function TemplateSelectionBlock({
13655
13739
  /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1.5 shrink-0 mr-1", children: [
13656
13740
  isLoadingTemplates && /* @__PURE__ */ jsx21(Loader25, { className: "w-3.5 h-3.5 text-emerald-600 animate-spin" }),
13657
13741
  /* @__PURE__ */ jsx21(
13658
- ChevronDown3,
13742
+ ChevronDown4,
13659
13743
  {
13660
13744
  className: `w-3.5 h-3.5 text-slate-400 transition-transform ${isDropdownOpen ? "rotate-180" : ""}`
13661
13745
  }
@@ -13927,7 +14011,7 @@ function TemplateSelectionBlock({
13927
14011
  ]
13928
14012
  }
13929
14013
  ),
13930
- /* @__PURE__ */ jsx21(ChevronDown3, { className: "w-3 h-3 text-slate-400 absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none" })
14014
+ /* @__PURE__ */ jsx21(ChevronDown4, { className: "w-3 h-3 text-slate-400 absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none" })
13931
14015
  ] })
13932
14016
  ]
13933
14017
  },
@@ -13943,7 +14027,7 @@ function TemplateSelectionBlock({
13943
14027
  import {
13944
14028
  Filter as Filter3,
13945
14029
  X as X10,
13946
- ChevronDown as ChevronDown4,
14030
+ ChevronDown as ChevronDown5,
13947
14031
  CheckSquare,
13948
14032
  RefreshCw
13949
14033
  } from "lucide-react";
@@ -14022,7 +14106,7 @@ function DynamicFiltersBlock({
14022
14106
  ]
14023
14107
  }
14024
14108
  ),
14025
- /* @__PURE__ */ jsx22(ChevronDown4, { className: "w-3 h-3 text-slate-400 absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none" })
14109
+ /* @__PURE__ */ jsx22(ChevronDown5, { className: "w-3 h-3 text-slate-400 absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none" })
14026
14110
  ] })
14027
14111
  ] }, col.name);
14028
14112
  }) }) })
@@ -14070,7 +14154,7 @@ function DynamicFiltersBlock({
14070
14154
  import {
14071
14155
  Phone as Phone3,
14072
14156
  Users,
14073
- ChevronDown as ChevronDown5,
14157
+ ChevronDown as ChevronDown6,
14074
14158
  Loader2 as Loader26,
14075
14159
  AlertCircle as AlertCircle4,
14076
14160
  Send,
@@ -14172,7 +14256,7 @@ function SettingsAndActionsBlock({
14172
14256
  children: targetPhoneOptions.map((opt) => /* @__PURE__ */ jsx23("option", { value: opt.id, children: opt.label }, opt.id))
14173
14257
  }
14174
14258
  ),
14175
- /* @__PURE__ */ jsx23(ChevronDown5, { className: "w-3 h-3 text-slate-400 absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none" })
14259
+ /* @__PURE__ */ jsx23(ChevronDown6, { className: "w-3 h-3 text-slate-400 absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none" })
14176
14260
  ] })
14177
14261
  ] }),
14178
14262
  /* @__PURE__ */ jsxs20("div", { className: "space-y-1", children: [
@@ -14276,7 +14360,7 @@ function SettingsAndActionsBlock({
14276
14360
  ]
14277
14361
  }
14278
14362
  ),
14279
- /* @__PURE__ */ jsx23(ChevronDown5, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14363
+ /* @__PURE__ */ jsx23(ChevronDown6, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14280
14364
  ] })
14281
14365
  ] }),
14282
14366
  selectedSubtableFilterCol && /* @__PURE__ */ jsxs20("div", { className: "space-y-1 animate-in fade-in duration-150", children: [
@@ -14307,7 +14391,7 @@ function SettingsAndActionsBlock({
14307
14391
  ]
14308
14392
  }
14309
14393
  ),
14310
- /* @__PURE__ */ jsx23(ChevronDown5, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14394
+ /* @__PURE__ */ jsx23(ChevronDown6, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14311
14395
  ] })
14312
14396
  ] }),
14313
14397
  /* @__PURE__ */ jsxs20("div", { className: "space-y-1", children: [
@@ -14326,7 +14410,7 @@ function SettingsAndActionsBlock({
14326
14410
  ]
14327
14411
  }
14328
14412
  ),
14329
- /* @__PURE__ */ jsx23(ChevronDown5, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14413
+ /* @__PURE__ */ jsx23(ChevronDown6, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14330
14414
  ] })
14331
14415
  ] })
14332
14416
  ] }),
@@ -14382,7 +14466,7 @@ function SettingsAndActionsBlock({
14382
14466
  ]
14383
14467
  }
14384
14468
  ),
14385
- /* @__PURE__ */ jsx23(ChevronDown5, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14469
+ /* @__PURE__ */ jsx23(ChevronDown6, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14386
14470
  ] })
14387
14471
  ] }),
14388
14472
  selectedPostSendOption && /* @__PURE__ */ jsxs20("div", { className: "space-y-1 animate-in fade-in duration-150", children: [
@@ -14413,7 +14497,7 @@ function SettingsAndActionsBlock({
14413
14497
  ]
14414
14498
  }
14415
14499
  ),
14416
- /* @__PURE__ */ jsx23(ChevronDown5, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14500
+ /* @__PURE__ */ jsx23(ChevronDown6, { className: "w-3 h-3 text-slate-400 absolute left-1.5 top-1/2 -translate-y-1/2 pointer-events-none" })
14417
14501
  ] })
14418
14502
  ] })
14419
14503
  ] }) })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@universe-platform/ui",
3
- "version": "0.1.0-beta.49",
3
+ "version": "0.1.0-beta.50",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",