@universe-platform/ui 0.1.0-beta.5 → 0.1.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -39,7 +39,7 @@ function cn(...inputs) {
39
39
  }
40
40
 
41
41
  // src/components/TableBlock/TableBlockClient.tsx
42
- import { useState as useState4, useMemo as useMemo2, useEffect as useEffect2, useCallback as useCallback3, useRef as useRef3 } from "react";
42
+ import React6, { useState as useState5, useMemo as useMemo2, useEffect as useEffect4, useCallback as useCallback4, useRef as useRef4 } from "react";
43
43
 
44
44
  // src/hooks/useToast.ts
45
45
  import { useContext as useContext2 } from "react";
@@ -242,12 +242,36 @@ function useToast() {
242
242
  import React4 from "react";
243
243
 
244
244
  // src/components/TableBlock/fields/DrivePickerCell.tsx
245
- import { useState as useState2 } from "react";
245
+ import React2, { useState as useState2, useEffect } from "react";
246
246
  import { FileText, Upload, X as X2 } from "lucide-react";
247
247
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
248
- function DrivePickerCell({ value, onChange, tableName, onMarkForDeletion, context }) {
248
+ function DrivePickerCell({ value, onChange, tableName, column, rowId, onMarkForDeletion, context }) {
249
249
  const [isOpening, setIsOpening] = useState2(false);
250
250
  const { showError } = useToast();
251
+ const cellContextId = React2.useMemo(() => {
252
+ const colName = (column == null ? void 0 : column.name) || "col";
253
+ const rId = rowId || "row";
254
+ return `${rId}-${colName}`;
255
+ }, [rowId, column == null ? void 0 : column.name]);
256
+ const fileList = React2.useMemo(() => {
257
+ if (!value) return [];
258
+ if (Array.isArray(value)) return value;
259
+ if (typeof value === "object" && (value.fileId || value.fileUrl || value.url)) {
260
+ return [value];
261
+ }
262
+ return [];
263
+ }, [value]);
264
+ useEffect(() => {
265
+ const handleMessage = (event) => {
266
+ var _a, _b, _c;
267
+ if (((_a = event.data) == null ? void 0 : _a.type) === "DRIVE_UPLOAD_SUCCESS" && ((_b = event.data) == null ? void 0 : _b.payload) && ((_c = event.data) == null ? void 0 : _c.cellContextId) === cellContextId) {
268
+ const incoming = Array.isArray(event.data.payload) ? event.data.payload : [event.data.payload];
269
+ onChange([...fileList, ...incoming]);
270
+ }
271
+ };
272
+ window.addEventListener("message", handleMessage);
273
+ return () => window.removeEventListener("message", handleMessage);
274
+ }, [fileList, onChange, cellContextId]);
251
275
  const openUploadPopup = async () => {
252
276
  if (isOpening) return;
253
277
  setIsOpening(true);
@@ -276,34 +300,35 @@ function DrivePickerCell({ value, onChange, tableName, onMarkForDeletion, contex
276
300
  } catch (e) {
277
301
  }
278
302
  }
279
- const actionResult = await context.executeAction("UPLOAD_DRIVE_FILE", { tableName });
303
+ const actionResult = await context.executeAction("UPLOAD_DRIVE_FILE", { tableName, cellContextId });
280
304
  const folderId = actionResult == null ? void 0 : actionResult.folderId;
281
- const pickerUrl = (actionResult == null ? void 0 : actionResult.pickerUrl) || (folderId ? `/embed/picker?folderId=${encodeURIComponent(folderId)}` : `/embed/picker`);
305
+ let pickerUrl = (actionResult == null ? void 0 : actionResult.pickerUrl) || (folderId ? `/embed/picker?folderId=${encodeURIComponent(folderId)}` : `/embed/picker`);
306
+ if (!pickerUrl.includes("cellContextId=")) {
307
+ const separator = pickerUrl.includes("?") ? "&" : "?";
308
+ pickerUrl = `${pickerUrl}${separator}cellContextId=${encodeURIComponent(cellContextId)}`;
309
+ }
282
310
  if (popup) {
283
311
  popup.location.href = pickerUrl;
284
312
  }
285
- const newFile = await new Promise((resolve) => {
313
+ await new Promise((resolve) => {
286
314
  let timer = null;
287
- const handleMessage = (event) => {
315
+ const handlePopupMessage = (event) => {
288
316
  var _a, _b;
289
- if (((_a = event.data) == null ? void 0 : _a.type) === "DRIVE_UPLOAD_SUCCESS" && ((_b = event.data) == null ? void 0 : _b.payload)) {
290
- window.removeEventListener("message", handleMessage);
317
+ if (((_a = event.data) == null ? void 0 : _a.type) === "DRIVE_UPLOAD_SUCCESS" && ((_b = event.data) == null ? void 0 : _b.cellContextId) === cellContextId) {
318
+ window.removeEventListener("message", handlePopupMessage);
291
319
  if (timer) clearInterval(timer);
292
- resolve(event.data.payload);
320
+ resolve();
293
321
  }
294
322
  };
295
323
  timer = setInterval(() => {
296
324
  if (!popup || popup.closed) {
297
325
  if (timer) clearInterval(timer);
298
- window.removeEventListener("message", handleMessage);
299
- resolve(null);
326
+ window.removeEventListener("message", handlePopupMessage);
327
+ resolve();
300
328
  }
301
329
  }, 800);
302
- window.addEventListener("message", handleMessage);
330
+ window.addEventListener("message", handlePopupMessage);
303
331
  });
304
- if (newFile) {
305
- onChange(newFile);
306
- }
307
332
  }
308
333
  } catch (error) {
309
334
  console.error("[DrivePickerCell] Error uploading file:", error);
@@ -313,40 +338,15 @@ function DrivePickerCell({ value, onChange, tableName, onMarkForDeletion, contex
313
338
  setIsOpening(false);
314
339
  }
315
340
  };
316
- if (value == null ? void 0 : value.fileUrl) {
317
- return /* @__PURE__ */ jsxs2("div", { className: "flex items-center w-full h-8 px-2 gap-1", children: [
318
- /* @__PURE__ */ jsxs2(
319
- "a",
320
- {
321
- href: value.fileUrl,
322
- target: "_blank",
323
- rel: "noopener noreferrer",
324
- className: "inline-flex items-center gap-1.5 max-w-[calc(100%-24px)] px-2 py-1 rounded-md text-xs font-medium text-blue-700 bg-blue-50 hover:bg-blue-100 transition-colors truncate",
325
- title: value.fileName || "Open file",
326
- children: [
327
- /* @__PURE__ */ jsx2(FileText, { className: "w-3.5 h-3.5 shrink-0" }),
328
- /* @__PURE__ */ jsx2("span", { className: "truncate", children: value.fileName || "Unnamed file" })
329
- ]
330
- }
331
- ),
332
- /* @__PURE__ */ jsx2(
333
- "button",
334
- {
335
- type: "button",
336
- onClick: () => {
337
- if ((value == null ? void 0 : value.fileId) && onMarkForDeletion) {
338
- onMarkForDeletion(value.fileId);
339
- }
340
- onChange(null);
341
- },
342
- className: "shrink-0 p-0.5 rounded hover:bg-rose-100 text-slate-400 hover:text-rose-500 transition-colors",
343
- title: "Remove file",
344
- children: /* @__PURE__ */ jsx2(X2, { className: "w-3.5 h-3.5" })
345
- }
346
- )
347
- ] });
348
- }
349
- return /* @__PURE__ */ jsx2("div", { className: "flex items-center w-full h-8 px-2", children: /* @__PURE__ */ jsxs2(
341
+ const handleRemoveFile = (indexToRemove) => {
342
+ const fileToRemove = fileList[indexToRemove];
343
+ if ((fileToRemove == null ? void 0 : fileToRemove.fileId) && onMarkForDeletion) {
344
+ onMarkForDeletion(fileToRemove.fileId);
345
+ }
346
+ const updated = fileList.filter((_, idx) => idx !== indexToRemove);
347
+ onChange(updated);
348
+ };
349
+ const renderUploadButton = () => /* @__PURE__ */ jsx2("div", { className: "flex items-center w-full h-8 px-2", children: /* @__PURE__ */ jsxs2(
350
350
  "button",
351
351
  {
352
352
  type: "button",
@@ -359,10 +359,42 @@ function DrivePickerCell({ value, onChange, tableName, onMarkForDeletion, contex
359
359
  ]
360
360
  }
361
361
  ) });
362
+ if (fileList.length > 0) {
363
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col w-full py-1 gap-1", children: [
364
+ fileList.map((file, idx) => /* @__PURE__ */ jsxs2("div", { className: "flex items-center w-full h-8 px-2 gap-1", children: [
365
+ /* @__PURE__ */ jsxs2(
366
+ "a",
367
+ {
368
+ href: file.fileUrl || file.url,
369
+ target: "_blank",
370
+ rel: "noopener noreferrer",
371
+ className: "inline-flex items-center gap-1.5 max-w-[calc(100%-24px)] px-2 py-1 rounded-md text-xs font-medium text-blue-700 bg-blue-50 hover:bg-blue-100 transition-colors truncate",
372
+ title: file.fileName || "Open file",
373
+ children: [
374
+ /* @__PURE__ */ jsx2(FileText, { className: "w-3.5 h-3.5 shrink-0" }),
375
+ /* @__PURE__ */ jsx2("span", { className: "truncate", children: file.fileName || "Unnamed file" })
376
+ ]
377
+ }
378
+ ),
379
+ /* @__PURE__ */ jsx2(
380
+ "button",
381
+ {
382
+ type: "button",
383
+ onClick: () => handleRemoveFile(idx),
384
+ className: "shrink-0 p-0.5 rounded hover:bg-rose-100 text-slate-400 hover:text-rose-500 transition-colors",
385
+ title: "Remove file",
386
+ children: /* @__PURE__ */ jsx2(X2, { className: "w-3.5 h-3.5" })
387
+ }
388
+ )
389
+ ] }, file.fileId || idx)),
390
+ renderUploadButton()
391
+ ] });
392
+ }
393
+ return renderUploadButton();
362
394
  }
363
395
 
364
396
  // src/components/TableBlock/fields/DatePickerCell.tsx
365
- import { useState as useState3, useRef as useRef2, useEffect, useMemo, useCallback as useCallback2 } from "react";
397
+ import { useState as useState3, useRef as useRef2, useEffect as useEffect2, useMemo, useCallback as useCallback2 } from "react";
366
398
  import { Calendar as CalendarIcon, ChevronLeft, ChevronRight, X as X3 } from "lucide-react";
367
399
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
368
400
  function parseDateParts(val) {
@@ -447,13 +479,13 @@ function DatePickerCell({ value, onChange, readOnly = false }) {
447
479
  const parsedParts = useMemo(() => parseDateParts(value), [value]);
448
480
  const displayString = useMemo(() => formatDateDisplay(parsedParts), [parsedParts]);
449
481
  const [rawInput, setRawInput] = useState3(displayString);
450
- useEffect(() => {
482
+ useEffect2(() => {
451
483
  setRawInput(displayString);
452
484
  }, [displayString]);
453
485
  const now = /* @__PURE__ */ new Date();
454
486
  const [viewYear, setViewYear] = useState3((parsedParts == null ? void 0 : parsedParts.year) || now.getFullYear());
455
487
  const [viewMonth, setViewMonth] = useState3((parsedParts == null ? void 0 : parsedParts.month) || now.getMonth() + 1);
456
- useEffect(() => {
488
+ useEffect2(() => {
457
489
  if (parsedParts) {
458
490
  setViewYear(parsedParts.year);
459
491
  setViewMonth(parsedParts.month);
@@ -478,7 +510,7 @@ function DatePickerCell({ value, onChange, readOnly = false }) {
478
510
  }
479
511
  }
480
512
  }, []);
481
- useEffect(() => {
513
+ useEffect2(() => {
482
514
  if (!isOpen) return;
483
515
  updatePopoverPosition();
484
516
  function handleScrollOrResize() {
@@ -704,15 +736,19 @@ var FORM_INPUTS = {
704
736
  DATE: ({ value, onChange, column }) => /* @__PURE__ */ jsx4(DatePickerCell, { value, onChange, column }),
705
737
  CURRENCY: ({ value, column, onChange }) => {
706
738
  const symbol = getCurrencySymbol(column.currencySymbol);
739
+ const [isFocused, setIsFocused] = React4.useState(false);
740
+ const displayValue = !isFocused && value !== void 0 && value !== null && value !== "" && !isNaN(Number(value)) ? new Intl.NumberFormat("en-US").format(Number(value)) : value !== void 0 && value !== null ? value : "";
707
741
  return /* @__PURE__ */ jsxs4("div", { className: "relative w-full h-full", children: [
708
742
  symbol && /* @__PURE__ */ jsx4("span", { className: "absolute left-2 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none text-sm", children: symbol }),
709
743
  /* @__PURE__ */ jsx4(
710
744
  "input",
711
745
  {
712
- type: "number",
746
+ type: isFocused ? "number" : "text",
713
747
  step: "any",
714
748
  className: `w-full h-8 ${symbol ? "pl-6 pr-2" : "px-2"} bg-transparent focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 text-sm`,
715
- value: value !== void 0 && value !== null ? value : "",
749
+ value: displayValue,
750
+ onFocus: () => setIsFocused(true),
751
+ onBlur: () => setIsFocused(false),
716
752
  onChange: (e) => {
717
753
  const val = e.target.value;
718
754
  onChange(val === "" ? "" : Number(val));
@@ -789,11 +825,13 @@ var FORM_INPUTS = {
789
825
  className: "w-4 h-4 rounded border-slate-300 text-brand"
790
826
  }
791
827
  ) }),
792
- GOOGLE_DRIVE_FILE: ({ value, onChange, context }) => /* @__PURE__ */ jsx4(
828
+ GOOGLE_DRIVE_FILE: ({ value, onChange, column, rowId, context }) => /* @__PURE__ */ jsx4(
793
829
  DrivePickerCell,
794
830
  {
795
- value: value || null,
831
+ value: value || [],
796
832
  onChange,
833
+ column,
834
+ rowId,
797
835
  tableName: context == null ? void 0 : context.tableName,
798
836
  onMarkForDeletion: context == null ? void 0 : context.onMarkForDeletion,
799
837
  context
@@ -851,6 +889,228 @@ var FORM_INPUTS = {
851
889
  }
852
890
  }
853
891
  ),
892
+ PHONE: ({ value, onChange }) => /* @__PURE__ */ jsx4(
893
+ "input",
894
+ {
895
+ type: "tel",
896
+ className: "w-full h-8 px-2 bg-transparent focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 text-sm",
897
+ value: value !== void 0 && value !== null ? String(value) : "",
898
+ onChange: (e) => onChange(e.target.value),
899
+ dir: "ltr"
900
+ }
901
+ ),
902
+ GOOGLE_CONTACT: ({ value, column, rowId, onChange, context }) => {
903
+ const [isSyncing, setIsSyncing] = React4.useState(false);
904
+ const { addToast } = useToast();
905
+ const googleClientIdRef = React4.useRef(null);
906
+ const ensureGisScript = React4.useCallback(() => {
907
+ return new Promise((resolve, reject) => {
908
+ var _a, _b, _c;
909
+ if ((_c = (_b = (_a = window.google) == null ? void 0 : _a.accounts) == null ? void 0 : _b.oauth2) == null ? void 0 : _c.initTokenClient) {
910
+ resolve();
911
+ return;
912
+ }
913
+ if (document.querySelector('script[src*="accounts.google.com/gsi/client"]')) {
914
+ const waitForLoad = setInterval(() => {
915
+ var _a2, _b2, _c2;
916
+ if ((_c2 = (_b2 = (_a2 = window.google) == null ? void 0 : _a2.accounts) == null ? void 0 : _b2.oauth2) == null ? void 0 : _c2.initTokenClient) {
917
+ clearInterval(waitForLoad);
918
+ resolve();
919
+ }
920
+ }, 100);
921
+ setTimeout(() => {
922
+ clearInterval(waitForLoad);
923
+ reject(new Error("GIS script load timeout"));
924
+ }, 1e4);
925
+ return;
926
+ }
927
+ const script = document.createElement("script");
928
+ script.src = "https://accounts.google.com/gsi/client";
929
+ script.async = true;
930
+ script.onload = () => resolve();
931
+ script.onerror = () => reject(new Error("Failed to load Google Identity Services"));
932
+ document.head.appendChild(script);
933
+ });
934
+ }, []);
935
+ const requestGisToken = React4.useCallback(async (hint) => {
936
+ await ensureGisScript();
937
+ if (!googleClientIdRef.current && (context == null ? void 0 : context.executeAction)) {
938
+ try {
939
+ const config = await context.executeAction("GET_GOOGLE_CONFIG", {});
940
+ googleClientIdRef.current = (config == null ? void 0 : config.clientId) || "";
941
+ } catch (e) {
942
+ throw new Error("Failed to fetch Google configuration");
943
+ }
944
+ }
945
+ const clientId = googleClientIdRef.current;
946
+ if (!clientId) {
947
+ throw new Error("Google Client ID is not configured");
948
+ }
949
+ const accessToken = await new Promise((resolve, reject) => {
950
+ const tokenClient = window.google.accounts.oauth2.initTokenClient({
951
+ client_id: clientId,
952
+ scope: "https://www.googleapis.com/auth/contacts https://www.googleapis.com/auth/userinfo.email",
953
+ prompt: hint ? "" : "select_account",
954
+ hint: hint || void 0,
955
+ callback: (tokenResponse) => {
956
+ if (tokenResponse == null ? void 0 : tokenResponse.access_token) {
957
+ resolve(tokenResponse.access_token);
958
+ } else {
959
+ reject(new Error("Google authorization was cancelled or failed"));
960
+ }
961
+ },
962
+ error_callback: (err) => {
963
+ reject(new Error((err == null ? void 0 : err.message) || "Google authorization error"));
964
+ }
965
+ });
966
+ tokenClient.requestAccessToken({ hint: hint || void 0 });
967
+ });
968
+ let email = "";
969
+ try {
970
+ const res = await fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
971
+ headers: { Authorization: `Bearer ${accessToken}` }
972
+ });
973
+ if (res.ok) {
974
+ const info = await res.json();
975
+ email = info.email || "";
976
+ }
977
+ } catch (e) {
978
+ }
979
+ return { accessToken, email };
980
+ }, [ensureGisScript, context]);
981
+ const handleSync = async () => {
982
+ var _a, _b, _c;
983
+ if (!(context == null ? void 0 : context.executeAction)) return;
984
+ setIsSyncing(true);
985
+ try {
986
+ const mapping = column.contactMapping || {};
987
+ const rowData = context.rowData || {};
988
+ let hasPhone = false;
989
+ if (mapping.phoneCol && rowData[mapping.phoneCol]) hasPhone = true;
990
+ let hasName = false;
991
+ if (mapping.nameParts && Array.isArray(mapping.nameParts)) {
992
+ for (const part of mapping.nameParts) {
993
+ if (part.type === "LOCAL" && rowData[part.columnId]) hasName = true;
994
+ if (part.type === "PARENT" && ((_a = context.parentRecord) == null ? void 0 : _a.tableId) === part.parentTableId && ((_c = (_b = context.parentRecord) == null ? void 0 : _b.data) == null ? void 0 : _c[part.columnId])) hasName = true;
995
+ }
996
+ }
997
+ if (!hasPhone) {
998
+ addToast({ type: "warning", message: "\u05E0\u05D0 \u05DC\u05DE\u05DC\u05D0 \u05DE\u05E1\u05E4\u05E8 \u05D8\u05DC\u05E4\u05D5\u05DF \u05DB\u05D3\u05D9 \u05DC\u05E1\u05E0\u05DB\u05E8\u05DF \u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8 \u05DC\u05D2\u05D5\u05D2\u05DC" });
999
+ setIsSyncing(false);
1000
+ return;
1001
+ }
1002
+ if (!hasName) {
1003
+ addToast({ type: "warning", message: "\u05E0\u05D0 \u05DC\u05DE\u05DC\u05D0 \u05E9\u05DD \u05DB\u05D3\u05D9 \u05DC\u05E1\u05E0\u05DB\u05E8\u05DF \u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8 \u05DC\u05D2\u05D5\u05D2\u05DC" });
1004
+ setIsSyncing(false);
1005
+ return;
1006
+ }
1007
+ let accessToken;
1008
+ let userEmail = "";
1009
+ try {
1010
+ const gisResult = await requestGisToken();
1011
+ accessToken = gisResult.accessToken;
1012
+ userEmail = gisResult.email;
1013
+ } catch (gisError) {
1014
+ addToast({ type: "warning", message: gisError.message || "\u05D4\u05D4\u05E8\u05E9\u05D0\u05D4 \u05DC\u05D2\u05D5\u05D2\u05DC \u05E0\u05DB\u05E9\u05DC\u05D4" });
1015
+ setIsSyncing(false);
1016
+ return;
1017
+ }
1018
+ const payload = {
1019
+ nameParts: mapping.nameParts || [],
1020
+ phoneCol: mapping.phoneCol,
1021
+ emailCol: mapping.emailCol,
1022
+ localRowData: rowData,
1023
+ parentRecord: context.parentRecord,
1024
+ accessToken,
1025
+ userEmail
1026
+ };
1027
+ const res = await context.executeAction("SYNC_GOOGLE_CONTACT", payload);
1028
+ if (res && res.synced) {
1029
+ if (context.updateQuietly) {
1030
+ context.updateQuietly(rowId, column.name, res);
1031
+ } else {
1032
+ onChange(res);
1033
+ }
1034
+ if (context.tableId && !rowId.startsWith("temp_")) {
1035
+ await context.executeAction("UPDATE_RECORD", {
1036
+ tableId: context.tableId,
1037
+ recordId: rowId,
1038
+ data: { [column.name]: res }
1039
+ });
1040
+ }
1041
+ }
1042
+ } catch (error) {
1043
+ console.error("Failed to sync contact:", error);
1044
+ addToast({ type: "warning", message: "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E1\u05E0\u05DB\u05E8\u05D5\u05DF \u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8: " + error.message });
1045
+ } finally {
1046
+ setIsSyncing(false);
1047
+ }
1048
+ };
1049
+ const handleUnlink = async () => {
1050
+ let accessToken;
1051
+ try {
1052
+ let emailHint = value == null ? void 0 : value.userEmail;
1053
+ if (!emailHint && (value == null ? void 0 : value.url)) {
1054
+ try {
1055
+ const parsed = new URL(value.url);
1056
+ emailHint = parsed.searchParams.get("authuser") || void 0;
1057
+ } catch (e) {
1058
+ }
1059
+ }
1060
+ const auth = await requestGisToken(emailHint);
1061
+ accessToken = auth.accessToken;
1062
+ } catch (err) {
1063
+ console.warn("[handleUnlink] Could not obtain token via GIS, falling back to server default token:", err);
1064
+ }
1065
+ if ((value == null ? void 0 : value.resourceName) && (context == null ? void 0 : context.executeAction)) {
1066
+ await context.executeAction("DELETE_GOOGLE_CONTACT", {
1067
+ resourceName: value.resourceName,
1068
+ accessToken
1069
+ });
1070
+ }
1071
+ if (context == null ? void 0 : context.updateQuietly) {
1072
+ context.updateQuietly(rowId, column.name, null);
1073
+ } else {
1074
+ onChange(null);
1075
+ }
1076
+ if ((context == null ? void 0 : context.tableId) && (context == null ? void 0 : context.executeAction) && !rowId.startsWith("temp_")) {
1077
+ await context.executeAction("UPDATE_RECORD", {
1078
+ tableId: context.tableId,
1079
+ recordId: rowId,
1080
+ data: { [column.name]: null }
1081
+ });
1082
+ }
1083
+ };
1084
+ if (value && value.synced && value.url) {
1085
+ return /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 h-8 px-2 group", children: [
1086
+ /* @__PURE__ */ jsx4("a", { href: value.url, target: "_blank", rel: "noopener noreferrer", className: "text-blue-600 hover:underline flex-1 truncate text-sm", children: "\u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8 \u05D2\u05D5\u05D2\u05DC" }),
1087
+ /* @__PURE__ */ jsx4(
1088
+ "button",
1089
+ {
1090
+ onClick: handleUnlink,
1091
+ className: "text-slate-400 hover:text-rose-500 opacity-0 group-hover:opacity-100 transition-opacity p-1",
1092
+ title: "\u05D1\u05D8\u05DC \u05E1\u05E0\u05DB\u05E8\u05D5\u05DF",
1093
+ children: /* @__PURE__ */ jsx4("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M6 18L18 6M6 6l12 12" }) })
1094
+ }
1095
+ )
1096
+ ] });
1097
+ }
1098
+ return /* @__PURE__ */ jsx4("div", { className: "flex items-center h-8 px-2", children: /* @__PURE__ */ jsxs4(
1099
+ "button",
1100
+ {
1101
+ onClick: handleSync,
1102
+ disabled: isSyncing,
1103
+ className: "flex items-center gap-1.5 px-3 py-1 bg-blue-50 text-blue-600 hover:bg-blue-100 border border-blue-200 rounded-md text-xs font-medium transition-colors disabled:opacity-50",
1104
+ children: [
1105
+ isSyncing ? /* @__PURE__ */ jsxs4("svg", { className: "w-3.5 h-3.5 animate-spin", viewBox: "0 0 24 24", fill: "none", children: [
1106
+ /* @__PURE__ */ jsx4("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1107
+ /* @__PURE__ */ jsx4("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
1108
+ ] }) : /* @__PURE__ */ jsx4("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M12 4v16m8-8H4" }) }),
1109
+ "\u05E1\u05E0\u05DB\u05E8\u05DF \u05DC\u05D2\u05D5\u05D2\u05DC"
1110
+ ]
1111
+ }
1112
+ ) });
1113
+ },
854
1114
  DEFAULT: ({ value, onChange }) => /* @__PURE__ */ jsx4(
855
1115
  "input",
856
1116
  {
@@ -859,11 +1119,459 @@ var FORM_INPUTS = {
859
1119
  value: value !== void 0 && value !== null ? String(value) : "",
860
1120
  onChange: (e) => onChange(e.target.value)
861
1121
  }
862
- )
1122
+ ),
1123
+ SUB_TABLE: ({ value, column }) => {
1124
+ if (!Array.isArray(value)) return /* @__PURE__ */ jsx4("span", { className: "px-2 py-1 flex items-center h-full text-slate-300 text-xs italic", children: "\u05E8\u05D9\u05E7" });
1125
+ const selected = value.filter((e) => e == null ? void 0 : e.isSelected);
1126
+ if (selected.length === 0) return /* @__PURE__ */ jsx4("span", { className: "px-2 py-1 flex items-center h-full text-slate-300 text-xs italic", children: "\u05E8\u05D9\u05E7" });
1127
+ const displayFields = column.subTableDisplayFields || [];
1128
+ return /* @__PURE__ */ jsx4("div", { className: "px-1.5 py-1 flex flex-wrap items-center gap-1 h-full", children: selected.map((entry, idx) => {
1129
+ var _a;
1130
+ let label = "";
1131
+ if (entry._resolvedData && displayFields.length > 0) {
1132
+ label = displayFields.map((f) => String(entry._resolvedData[f] || "")).filter(Boolean).join(" ");
1133
+ }
1134
+ if (!label) label = ((_a = entry.recordId) == null ? void 0 : _a.slice(-6)) || "...";
1135
+ return /* @__PURE__ */ jsx4(
1136
+ "span",
1137
+ {
1138
+ className: "inline-flex items-center px-1.5 py-0.5 bg-amber-50 text-amber-800 border border-amber-200 rounded-full text-xs font-medium max-w-[140px] truncate",
1139
+ title: label,
1140
+ children: label
1141
+ },
1142
+ entry.recordId || idx
1143
+ );
1144
+ }) });
1145
+ }
863
1146
  };
864
1147
 
865
- // src/components/TableBlock/TableBlockClient.tsx
1148
+ // src/components/TableBlock/fields/SubTableViewer.tsx
1149
+ import { useState as useState4, useEffect as useEffect3, useRef as useRef3, useCallback as useCallback3 } from "react";
866
1150
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1151
+ function SubTableViewer({ rowId, column, value, onChange, context }) {
1152
+ var _a;
1153
+ const { addToast } = useToast();
1154
+ const entries = Array.isArray(value) ? value : [];
1155
+ const [linkedRecords, setLinkedRecords] = useState4([]);
1156
+ const [targetSchema, setTargetSchema] = useState4(null);
1157
+ const [isLoading, setIsLoading] = useState4(true);
1158
+ const [isSearchOpen, setIsSearchOpen] = useState4(false);
1159
+ const [searchQuery, setSearchQuery] = useState4("");
1160
+ const [searchResults, setSearchResults] = useState4([]);
1161
+ const [isSearching, setIsSearching] = useState4(false);
1162
+ const [isCreating, setIsCreating] = useState4(false);
1163
+ const [newRecordData, setNewRecordData] = useState4({});
1164
+ const searchRef = useRef3(null);
1165
+ const searchTimerRef = useRef3(null);
1166
+ const executeAction = context == null ? void 0 : context.executeAction;
1167
+ const referencedTableId = column.referencedTableId;
1168
+ const fetchedIdsRef = useRef3(/* @__PURE__ */ new Set());
1169
+ useEffect3(() => {
1170
+ if (!referencedTableId || !executeAction) {
1171
+ setIsLoading(false);
1172
+ return;
1173
+ }
1174
+ const recordIdsToFetch = entries.map((e) => e.recordId).filter((id) => !fetchedIdsRef.current.has(id));
1175
+ if (recordIdsToFetch.length === 0 && targetSchema) return;
1176
+ if (recordIdsToFetch.length > 0) {
1177
+ recordIdsToFetch.forEach((id) => fetchedIdsRef.current.add(id));
1178
+ }
1179
+ setIsLoading(true);
1180
+ executeAction("FETCH_SUB_TABLE_RECORDS", { referencedTableId, recordIds: recordIdsToFetch }).then((res) => {
1181
+ const payload = (res == null ? void 0 : res.data) || res;
1182
+ if (payload == null ? void 0 : payload.records) {
1183
+ setLinkedRecords((prev) => {
1184
+ const map = new Map(prev.map((r) => [r.id, r]));
1185
+ payload.records.forEach((r) => map.set(r.id, r));
1186
+ return Array.from(map.values());
1187
+ });
1188
+ }
1189
+ if (payload == null ? void 0 : payload.schema) {
1190
+ setTargetSchema(payload.schema);
1191
+ }
1192
+ }).catch(() => {
1193
+ recordIdsToFetch.forEach((id) => fetchedIdsRef.current.delete(id));
1194
+ }).finally(() => setIsLoading(false));
1195
+ }, [referencedTableId, executeAction, entries, targetSchema]);
1196
+ useEffect3(() => {
1197
+ const handleClick = (e) => {
1198
+ if (searchRef.current && !searchRef.current.contains(e.target)) {
1199
+ setIsSearchOpen(false);
1200
+ }
1201
+ };
1202
+ if (isSearchOpen) document.addEventListener("mousedown", handleClick);
1203
+ return () => document.removeEventListener("mousedown", handleClick);
1204
+ }, [isSearchOpen]);
1205
+ const handleSearch = useCallback3((query) => {
1206
+ setSearchQuery(query);
1207
+ if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
1208
+ if (!query.trim()) {
1209
+ setSearchResults([]);
1210
+ return;
1211
+ }
1212
+ searchTimerRef.current = setTimeout(async () => {
1213
+ if (!executeAction || !referencedTableId) return;
1214
+ setIsSearching(true);
1215
+ try {
1216
+ const excludeIds = entries.map((e) => e.recordId);
1217
+ const res = await executeAction("SEARCH_SUB_TABLE_RECORDS", {
1218
+ referencedTableId,
1219
+ query: query.trim(),
1220
+ excludeIds,
1221
+ limit: 15
1222
+ });
1223
+ setSearchResults(Array.isArray(res) ? res : []);
1224
+ } catch (e) {
1225
+ setSearchResults([]);
1226
+ } finally {
1227
+ setIsSearching(false);
1228
+ }
1229
+ }, 300);
1230
+ }, [executeAction, referencedTableId, entries]);
1231
+ const targetColumns = ((targetSchema == null ? void 0 : targetSchema.columns) || []).filter(
1232
+ (c) => {
1233
+ if (c.name === "_id" || c.type === "SUB_TABLE") return false;
1234
+ return true;
1235
+ }
1236
+ );
1237
+ const [relationOptions, setRelationOptions] = useState4({});
1238
+ useEffect3(() => {
1239
+ if (!executeAction || targetColumns.length === 0) return;
1240
+ const relationCols = targetColumns.filter((c) => {
1241
+ var _a2;
1242
+ return ((_a2 = c.type) == null ? void 0 : _a2.toUpperCase()) === "RELATION" && c.referencedTableId;
1243
+ });
1244
+ relationCols.forEach(async (col) => {
1245
+ try {
1246
+ const options = await executeAction("FETCH_RELATION", {
1247
+ referencedTableId: col.referencedTableId,
1248
+ displayField: col.displayField
1249
+ });
1250
+ if (options && Array.isArray(options)) {
1251
+ setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: options }));
1252
+ }
1253
+ } catch (err) {
1254
+ console.warn(`[SubTableViewer] Could not fetch relation options for column ${col.name}:`, err);
1255
+ }
1256
+ });
1257
+ }, [executeAction, targetColumns]);
1258
+ const refetchRelation = useCallback3(async (colName) => {
1259
+ var _a2;
1260
+ const col = targetColumns.find((c) => c.name === colName);
1261
+ if (!col || ((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) !== "RELATION" || !col.referencedTableId || !executeAction) return;
1262
+ try {
1263
+ const options = await executeAction("FETCH_RELATION", { referencedTableId: col.referencedTableId, displayField: col.displayField });
1264
+ if (options && Array.isArray(options)) {
1265
+ setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: options }));
1266
+ }
1267
+ } catch (err) {
1268
+ console.warn(`[SubTableViewer] Could not refetch relation options for column ${col.name}:`, err);
1269
+ }
1270
+ }, [targetColumns, executeAction]);
1271
+ const updateParentLinkage = async (updated) => {
1272
+ const isNewRow = rowId.startsWith("temp_");
1273
+ if (context == null ? void 0 : context.updateQuietly) {
1274
+ context.updateQuietly(rowId, column.name, updated);
1275
+ } else {
1276
+ onChange(updated);
1277
+ }
1278
+ if (executeAction && (context == null ? void 0 : context.parentTableId) && !isNewRow) {
1279
+ try {
1280
+ const payloadToSave = updated.map(({ recordId, isSelected }) => ({ recordId, isSelected }));
1281
+ await executeAction("UPDATE_RECORD", {
1282
+ tableId: context.parentTableId,
1283
+ recordId: rowId,
1284
+ data: { [column.name]: payloadToSave }
1285
+ });
1286
+ if (context == null ? void 0 : context.triggerSaveSuccess) {
1287
+ context.triggerSaveSuccess();
1288
+ }
1289
+ } catch (err) {
1290
+ console.error("Failed to auto-save parent linkage", err);
1291
+ }
1292
+ }
1293
+ };
1294
+ const handleLinkRecord = (record) => {
1295
+ fetchedIdsRef.current.add(record.id);
1296
+ const newEntry = { recordId: record.id, isSelected: true, _resolvedData: record.data };
1297
+ const updated = [...entries, newEntry];
1298
+ updateParentLinkage(updated);
1299
+ setLinkedRecords((prev) => {
1300
+ const exists = prev.find((r) => r.id === record.id);
1301
+ if (exists) return prev;
1302
+ return [...prev, record];
1303
+ });
1304
+ setSearchQuery("");
1305
+ setSearchResults([]);
1306
+ setIsSearchOpen(false);
1307
+ };
1308
+ const handleUnlinkRecord = (recordId) => {
1309
+ const updated = entries.filter((e) => e.recordId !== recordId);
1310
+ updateParentLinkage(updated);
1311
+ setLinkedRecords((prev) => prev.filter((r) => r.id !== recordId));
1312
+ };
1313
+ const handleToggleSelected = (recordId) => {
1314
+ const record = linkedRecords.find((r) => r.id === recordId);
1315
+ const updated = entries.map(
1316
+ (e) => e.recordId === recordId ? __spreadProps(__spreadValues({}, e), { isSelected: !e.isSelected, _resolvedData: (record == null ? void 0 : record.data) || e._resolvedData }) : e
1317
+ );
1318
+ updateParentLinkage(updated);
1319
+ };
1320
+ const handleRecordCellChange = async (recordId, colName, value2) => {
1321
+ const updatedRecords = linkedRecords.map(
1322
+ (r) => r.id === recordId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value2 }) }) : r
1323
+ );
1324
+ setLinkedRecords(updatedRecords);
1325
+ const targetRecord = updatedRecords.find((r) => r.id === recordId);
1326
+ const updatedEntries = entries.map(
1327
+ (e) => e.recordId === recordId ? __spreadProps(__spreadValues({}, e), { _resolvedData: targetRecord == null ? void 0 : targetRecord.data }) : e
1328
+ );
1329
+ if (context == null ? void 0 : context.updateQuietly) {
1330
+ context.updateQuietly(rowId, column.name, updatedEntries);
1331
+ } else {
1332
+ onChange(updatedEntries);
1333
+ }
1334
+ if (executeAction && referencedTableId && !recordId.startsWith("temp_")) {
1335
+ try {
1336
+ await executeAction("UPDATE_RECORD", {
1337
+ tableId: referencedTableId,
1338
+ recordId,
1339
+ data: { [colName]: value2 }
1340
+ });
1341
+ if (context == null ? void 0 : context.triggerSaveSuccess) {
1342
+ context.triggerSaveSuccess();
1343
+ }
1344
+ } catch (err) {
1345
+ console.error("[SubTableViewer] Failed to auto-save linked record field", err);
1346
+ }
1347
+ }
1348
+ };
1349
+ const handleCreateRecord = async () => {
1350
+ var _a2;
1351
+ if (!executeAction || !referencedTableId) return;
1352
+ for (const col of targetColumns) {
1353
+ if (col.isRequired) {
1354
+ const val = newRecordData[col.name];
1355
+ if (val === void 0 || val === null || typeof val === "string" && val.trim() === "") {
1356
+ addToast({
1357
+ type: "error",
1358
+ message: `Validation Error: Field '${col.name}' is required but was not provided.`
1359
+ });
1360
+ return;
1361
+ }
1362
+ }
1363
+ }
1364
+ try {
1365
+ const res = await executeAction("CREATE_RECORD", {
1366
+ tableId: referencedTableId,
1367
+ records: [newRecordData]
1368
+ });
1369
+ if (res && res.success === false) {
1370
+ throw new Error(res.error || "Action execution failed");
1371
+ }
1372
+ const created = Array.isArray(res) ? res[0] : ((_a2 = res == null ? void 0 : res.data) == null ? void 0 : _a2[0]) || res;
1373
+ if (created) {
1374
+ const newRecord = {
1375
+ id: String(created.id || created._id),
1376
+ data: created.data || newRecordData
1377
+ };
1378
+ handleLinkRecord(newRecord);
1379
+ setIsCreating(false);
1380
+ setNewRecordData({});
1381
+ }
1382
+ } catch (err) {
1383
+ const msg = (err == null ? void 0 : err.message) || String(err);
1384
+ addToast({
1385
+ type: "error",
1386
+ message: msg
1387
+ });
1388
+ }
1389
+ };
1390
+ const getDisplayLabel = (record) => {
1391
+ const fields = column.subTableDisplayFields || [];
1392
+ if (fields.length === 0) {
1393
+ const firstField = Object.keys(record.data).find((k) => k !== "_id");
1394
+ return firstField ? String(record.data[firstField] || "") : record.id;
1395
+ }
1396
+ return fields.map((f) => String(record.data[f] || "")).filter(Boolean).join(" ");
1397
+ };
1398
+ return /* @__PURE__ */ jsxs5("div", { className: "p-3 animate-in fade-in slide-in-from-top-2 duration-200", dir: "rtl", children: [
1399
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-4 mb-2.5", children: [
1400
+ /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx5("span", { className: "text-xs font-bold text-slate-600", children: (_a = targetSchema == null ? void 0 : targetSchema.name) != null ? _a : column.referencedTableName }) }),
1401
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1.5", children: [
1402
+ /* @__PURE__ */ jsxs5("div", { className: "relative", ref: searchRef, children: [
1403
+ /* @__PURE__ */ jsxs5(
1404
+ "button",
1405
+ {
1406
+ type: "button",
1407
+ onClick: () => setIsSearchOpen((prev) => !prev),
1408
+ className: "h-7 px-2.5 text-[11px] font-semibold bg-blue-50 hover:bg-blue-100 text-blue-700 border border-blue-200 rounded-md transition-colors cursor-pointer flex items-center gap-1",
1409
+ children: [
1410
+ /* @__PURE__ */ jsx5("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }),
1411
+ "\u05E7\u05D9\u05E9\u05D5\u05E8 \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA"
1412
+ ]
1413
+ }
1414
+ ),
1415
+ isSearchOpen && /* @__PURE__ */ jsxs5("div", { className: "absolute right-0 top-full mt-1 w-72 bg-white border border-slate-200 rounded-lg shadow-xl z-50 p-2", children: [
1416
+ /* @__PURE__ */ jsx5(
1417
+ "input",
1418
+ {
1419
+ type: "text",
1420
+ value: searchQuery,
1421
+ onChange: (e) => handleSearch(e.target.value),
1422
+ placeholder: "\u05D7\u05E4\u05E9 \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA...",
1423
+ className: "w-full h-8 px-2.5 text-xs border border-slate-300 rounded-md focus:outline-none focus:ring-1 focus:ring-blue-400 mb-1.5",
1424
+ autoFocus: true
1425
+ }
1426
+ ),
1427
+ /* @__PURE__ */ jsx5("div", { className: "max-h-48 overflow-y-auto", children: isSearching ? /* @__PURE__ */ jsx5("div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ jsx5("div", { className: "w-4 h-4 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" }) }) : searchResults.length > 0 ? searchResults.map((r) => /* @__PURE__ */ jsx5(
1428
+ "button",
1429
+ {
1430
+ type: "button",
1431
+ onClick: () => handleLinkRecord(r),
1432
+ className: "w-full text-right px-2 py-1.5 text-xs hover:bg-blue-50 rounded cursor-pointer transition-colors",
1433
+ children: getDisplayLabel(r)
1434
+ },
1435
+ r.id
1436
+ )) : searchQuery.trim() ? /* @__PURE__ */ jsx5("p", { className: "text-xs text-slate-400 text-center py-3", children: "\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA" }) : /* @__PURE__ */ jsx5("p", { className: "text-xs text-slate-400 text-center py-3", children: "\u05D4\u05E7\u05DC\u05D3 \u05DC\u05D7\u05D9\u05E4\u05D5\u05E9..." }) })
1437
+ ] })
1438
+ ] }),
1439
+ /* @__PURE__ */ jsxs5(
1440
+ "button",
1441
+ {
1442
+ type: "button",
1443
+ onClick: () => {
1444
+ setIsCreating(true);
1445
+ const initialData = {};
1446
+ targetColumns.forEach((c) => {
1447
+ if (c.defaultValue !== void 0 && c.defaultValue !== null) {
1448
+ initialData[c.name] = c.type === "NUMBER" || c.type === "CURRENCY" ? parseFloat(c.defaultValue) : c.type === "BOOLEAN" ? c.defaultValue === "true" || c.defaultValue === true : c.defaultValue;
1449
+ } else {
1450
+ if (c.type === "NUMBER" || c.type === "CURRENCY") initialData[c.name] = 0;
1451
+ else if (c.type === "BOOLEAN") initialData[c.name] = false;
1452
+ else initialData[c.name] = "";
1453
+ }
1454
+ });
1455
+ setNewRecordData(initialData);
1456
+ },
1457
+ className: "h-7 px-2.5 text-[11px] font-semibold bg-emerald-50 hover:bg-emerald-100 text-emerald-700 border border-emerald-200 rounded-md transition-colors cursor-pointer flex items-center gap-1",
1458
+ children: [
1459
+ /* @__PURE__ */ jsx5("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M12 4v16m8-8H4" }) }),
1460
+ "\u05E8\u05E9\u05D5\u05DE\u05D4 \u05D7\u05D3\u05E9\u05D4"
1461
+ ]
1462
+ }
1463
+ )
1464
+ ] })
1465
+ ] }),
1466
+ isLoading ? /* @__PURE__ */ jsx5("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx5("div", { className: "w-5 h-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" }) }) : entries.length === 0 && !isCreating ? /* @__PURE__ */ jsx5("div", { className: "text-center py-6 text-xs text-slate-400", children: "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA. \u05D4\u05E9\u05EA\u05DE\u05E9 \u05D1\u05D7\u05D9\u05E4\u05D5\u05E9 \u05DB\u05D3\u05D9 \u05DC\u05E7\u05E9\u05E8 \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05E7\u05D9\u05D9\u05DE\u05D5\u05EA, \u05D0\u05D5 \u05D4\u05D5\u05E1\u05E3 \u05E8\u05E9\u05D5\u05DE\u05D4 \u05D7\u05D3\u05E9\u05D4." }) : /* @__PURE__ */ jsx5("div", { className: "overflow-x-auto pb-1", children: /* @__PURE__ */ jsxs5("table", { className: "w-full text-xs border-collapse", children: [
1467
+ /* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { className: "bg-slate-100/80", children: [
1468
+ /* @__PURE__ */ jsx5("th", { className: "border border-slate-200 px-2 py-1.5 text-center w-10 font-semibold text-slate-500", children: "\u05E0\u05D1\u05D7\u05E8" }),
1469
+ targetColumns.map((col) => /* @__PURE__ */ jsx5("th", { className: "border border-slate-200 px-2 py-1.5 text-right font-semibold text-slate-600 whitespace-nowrap", children: col.name }, col.name)),
1470
+ /* @__PURE__ */ jsx5("th", { className: "border border-slate-200 px-2 py-1.5 w-14" })
1471
+ ] }) }),
1472
+ /* @__PURE__ */ jsxs5("tbody", { children: [
1473
+ entries.map((entry) => {
1474
+ const record = linkedRecords.find((r) => r.id === entry.recordId);
1475
+ return /* @__PURE__ */ jsxs5("tr", { className: "hover:bg-blue-50/30 transition-colors", children: [
1476
+ /* @__PURE__ */ jsx5("td", { className: "border border-slate-200 px-2 py-1 text-center", children: /* @__PURE__ */ jsx5(
1477
+ "input",
1478
+ {
1479
+ type: "checkbox",
1480
+ checked: entry.isSelected,
1481
+ onChange: () => handleToggleSelected(entry.recordId),
1482
+ className: "w-3.5 h-3.5 rounded border-slate-300 text-blue-600 cursor-pointer"
1483
+ }
1484
+ ) }),
1485
+ targetColumns.map((col) => {
1486
+ var _a2, _b;
1487
+ const colType = ((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) || "DEFAULT";
1488
+ const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
1489
+ return /* @__PURE__ */ jsx5("td", { className: "border border-slate-200 p-0 relative h-8", children: record ? /* @__PURE__ */ jsx5(
1490
+ InputComponent,
1491
+ {
1492
+ value: (_b = record.data[col.name]) != null ? _b : colType === "BOOLEAN" ? false : "",
1493
+ column: col,
1494
+ rowId: record.id,
1495
+ onChange: (v) => handleRecordCellChange(record.id, col.name, v),
1496
+ context: __spreadProps(__spreadValues({}, context), {
1497
+ tableName: (targetSchema == null ? void 0 : targetSchema.name) || (targetSchema == null ? void 0 : targetSchema.tableName) || (targetSchema == null ? void 0 : targetSchema.title) || (targetSchema == null ? void 0 : targetSchema.id) || "Table",
1498
+ relationOptions,
1499
+ updateRelationOptions: (name, opts) => setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [name]: opts })),
1500
+ refetchRelation,
1501
+ parentRecord: { tableId: (context == null ? void 0 : context.parentTableId) || (context == null ? void 0 : context.tableId), data: context == null ? void 0 : context.rowData },
1502
+ rowData: record.data,
1503
+ tableId: targetSchema == null ? void 0 : targetSchema.id,
1504
+ updateQuietly: (rid, colName, val) => handleRecordCellChange(rid, colName, val)
1505
+ })
1506
+ }
1507
+ ) : /* @__PURE__ */ jsx5("span", { className: "text-slate-300 italic px-2", children: "\u05D8\u05D5\u05E2\u05DF..." }) }, col.name);
1508
+ }),
1509
+ /* @__PURE__ */ jsx5("td", { className: "border border-slate-200 px-1 py-1 text-center", children: /* @__PURE__ */ jsx5(
1510
+ "button",
1511
+ {
1512
+ type: "button",
1513
+ onClick: () => handleUnlinkRecord(entry.recordId),
1514
+ className: "text-slate-400 hover:text-rose-500 p-0.5 rounded transition-colors cursor-pointer inline-flex items-center justify-center w-full",
1515
+ title: "\u05D4\u05E1\u05E8 \u05E7\u05D9\u05E9\u05D5\u05E8",
1516
+ children: /* @__PURE__ */ jsx5("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M6 18L18 6M6 6l12 12" }) })
1517
+ }
1518
+ ) })
1519
+ ] }, entry.recordId);
1520
+ }),
1521
+ isCreating && /* @__PURE__ */ jsxs5("tr", { className: "bg-emerald-50/40", children: [
1522
+ /* @__PURE__ */ jsx5("td", { className: "border border-emerald-200 px-2 py-1 text-center", children: /* @__PURE__ */ jsx5("span", { className: "text-emerald-500 font-bold text-[10px]", children: "\u05D7\u05D3\u05E9" }) }),
1523
+ targetColumns.map((col) => {
1524
+ var _a2, _b;
1525
+ const colType = ((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) || "DEFAULT";
1526
+ const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
1527
+ return /* @__PURE__ */ jsx5("td", { className: "border border-emerald-200 p-0 relative h-8", children: /* @__PURE__ */ jsx5(
1528
+ InputComponent,
1529
+ {
1530
+ value: (_b = newRecordData[col.name]) != null ? _b : colType === "BOOLEAN" ? false : "",
1531
+ column: col,
1532
+ rowId: "new_record",
1533
+ onChange: (v) => setNewRecordData((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: v })),
1534
+ context: __spreadProps(__spreadValues({}, context), {
1535
+ tableName: (targetSchema == null ? void 0 : targetSchema.name) || (targetSchema == null ? void 0 : targetSchema.tableName) || (targetSchema == null ? void 0 : targetSchema.title) || (targetSchema == null ? void 0 : targetSchema.id) || "Table",
1536
+ relationOptions,
1537
+ refetchRelation
1538
+ })
1539
+ }
1540
+ ) }, `new_${col.name}`);
1541
+ }),
1542
+ /* @__PURE__ */ jsx5("td", { className: "border border-emerald-200 px-1 py-1 text-center", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-center gap-1", children: [
1543
+ /* @__PURE__ */ jsx5(
1544
+ "button",
1545
+ {
1546
+ type: "button",
1547
+ onClick: handleCreateRecord,
1548
+ className: "p-1 text-emerald-600 hover:bg-emerald-100 rounded transition-colors",
1549
+ title: "\u05E9\u05DE\u05D5\u05E8",
1550
+ children: /* @__PURE__ */ jsx5("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M5 13l4 4L19 7" }) })
1551
+ }
1552
+ ),
1553
+ /* @__PURE__ */ jsx5(
1554
+ "button",
1555
+ {
1556
+ type: "button",
1557
+ onClick: () => {
1558
+ setIsCreating(false);
1559
+ },
1560
+ className: "p-1 text-rose-500 hover:bg-rose-100 rounded transition-colors",
1561
+ title: "\u05D1\u05D9\u05D8\u05D5\u05DC",
1562
+ children: /* @__PURE__ */ jsx5("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M6 18L18 6M6 6l12 12" }) })
1563
+ }
1564
+ )
1565
+ ] }) })
1566
+ ] })
1567
+ ] })
1568
+ ] }) })
1569
+ ] });
1570
+ }
1571
+
1572
+ // src/components/TableBlock/TableBlockClient.tsx
1573
+ import { X as X4, Copy as Copy2 } from "lucide-react";
1574
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
867
1575
  function TableBlockClient({
868
1576
  data: initialRecords,
869
1577
  settings: schema,
@@ -880,6 +1588,68 @@ function TableBlockClient({
880
1588
  "data-testid": dataTestId
881
1589
  }) {
882
1590
  const { showError, addToast } = useToast();
1591
+ const [columnWidths, setColumnWidths] = useState5({});
1592
+ const [isMounted, setIsMounted] = useState5(false);
1593
+ const tableKey = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id) || (schema == null ? void 0 : schema.name) || "default";
1594
+ const [page, setPage] = useState5(1);
1595
+ const [limit, setLimit] = useState5(50);
1596
+ const [totalRecords, setTotalRecords] = useState5(0);
1597
+ const [totalPages, setTotalPages] = useState5(1);
1598
+ useEffect4(() => {
1599
+ if (typeof window !== "undefined") {
1600
+ const savedWidths = localStorage.getItem(`table_widths_${tableKey}`);
1601
+ if (savedWidths) {
1602
+ try {
1603
+ setColumnWidths(JSON.parse(savedWidths));
1604
+ } catch (e) {
1605
+ }
1606
+ }
1607
+ const savedSummary = localStorage.getItem(`table_summary_${tableKey}`);
1608
+ if (savedSummary !== null) {
1609
+ setShowSummary(savedSummary === "true");
1610
+ }
1611
+ const savedLimit = localStorage.getItem(`table_limit_${tableKey}`);
1612
+ if (savedLimit) {
1613
+ const parsed = parseInt(savedLimit, 10);
1614
+ if ([20, 50, 100].includes(parsed)) {
1615
+ setLimit(parsed);
1616
+ }
1617
+ }
1618
+ }
1619
+ setIsMounted(true);
1620
+ }, [tableKey]);
1621
+ const [showSummary, setShowSummary] = useState5(false);
1622
+ const toggleSummary = () => {
1623
+ setShowSummary((prev) => {
1624
+ const next = !prev;
1625
+ const tableKey2 = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id) || (schema == null ? void 0 : schema.name) || "default";
1626
+ localStorage.setItem(`table_summary_${tableKey2}`, String(next));
1627
+ return next;
1628
+ });
1629
+ };
1630
+ const [resizingCol, setResizingCol] = useState5(null);
1631
+ useEffect4(() => {
1632
+ if (!resizingCol) return;
1633
+ const handleMouseMove = (e) => {
1634
+ const diffX = resizingCol.startX - e.clientX;
1635
+ const newWidth = Math.max(100, Math.min(500, resizingCol.startWidth + diffX));
1636
+ setColumnWidths((prev) => __spreadProps(__spreadValues({}, prev), { [resizingCol.name]: newWidth }));
1637
+ };
1638
+ const handleMouseUp = () => {
1639
+ setResizingCol(null);
1640
+ setColumnWidths((prev) => {
1641
+ const tableKey2 = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id) || (schema == null ? void 0 : schema.name) || "default";
1642
+ localStorage.setItem(`table_widths_${tableKey2}`, JSON.stringify(prev));
1643
+ return prev;
1644
+ });
1645
+ };
1646
+ document.addEventListener("mousemove", handleMouseMove);
1647
+ document.addEventListener("mouseup", handleMouseUp);
1648
+ return () => {
1649
+ document.removeEventListener("mousemove", handleMouseMove);
1650
+ document.removeEventListener("mouseup", handleMouseUp);
1651
+ };
1652
+ }, [resizingCol, schema]);
883
1653
  const normalizeRecords = (records) => {
884
1654
  return (records || []).map((row) => {
885
1655
  var _c;
@@ -905,18 +1675,38 @@ function TableBlockClient({
905
1675
  return __spreadProps(__spreadValues({}, row), { id: String(row.id || ""), data: rowData });
906
1676
  });
907
1677
  };
908
- const [localColumns, setLocalColumns] = useState4(() => (schema == null ? void 0 : schema.columns) || []);
909
- useEffect2(() => {
1678
+ const mergeColumnDefs = useCallback4((existingCols, incomingCols) => {
1679
+ if (!incomingCols || incomingCols.length === 0) return existingCols || [];
1680
+ if (!existingCols || existingCols.length === 0) return incomingCols;
1681
+ const existingMap = new Map(existingCols.map((c) => [c.name, c]));
1682
+ const merged = incomingCols.map((inc) => {
1683
+ const ext = existingMap.get(inc.name);
1684
+ if (ext) {
1685
+ existingMap.delete(inc.name);
1686
+ return __spreadValues(__spreadValues({}, ext), inc);
1687
+ }
1688
+ return inc;
1689
+ });
1690
+ for (const [, extCol] of existingMap) {
1691
+ merged.push(extCol);
1692
+ }
1693
+ return merged.sort((a, b) => {
1694
+ var _a, _b;
1695
+ return ((_a = a.order) != null ? _a : 0) - ((_b = b.order) != null ? _b : 0);
1696
+ });
1697
+ }, []);
1698
+ const [localColumns, setLocalColumns] = useState5(() => (schema == null ? void 0 : schema.columns) || []);
1699
+ useEffect4(() => {
910
1700
  if ((schema == null ? void 0 : schema.columns) && schema.columns.length > 0) {
911
- setLocalColumns(schema.columns);
1701
+ setLocalColumns((prev) => mergeColumnDefs(prev, schema.columns));
912
1702
  }
913
- }, [schema == null ? void 0 : schema.columns]);
914
- const [dirty, setDirty] = useState4({ added: [], modified: {}, deleted: [] });
915
- const dirtyRef = useRef3(dirty);
916
- useEffect2(() => {
1703
+ }, [schema == null ? void 0 : schema.columns, mergeColumnDefs]);
1704
+ const [dirty, setDirty] = useState5({ added: [], modified: {}, deleted: [] });
1705
+ const dirtyRef = useRef4(dirty);
1706
+ useEffect4(() => {
917
1707
  dirtyRef.current = dirty;
918
1708
  }, [dirty]);
919
- const mergeWithDirty = useCallback3((baseRecords) => {
1709
+ const mergeWithDirty = useCallback4((baseRecords) => {
920
1710
  const { added, modified, deleted } = dirtyRef.current;
921
1711
  const nonDeleted = (baseRecords || []).filter((r) => !deleted.includes(r.id));
922
1712
  const modifiedRecords = nonDeleted.map((r) => {
@@ -929,8 +1719,8 @@ function TableBlockClient({
929
1719
  const uniqueAdded = added.filter((r) => !baseIds.has(r.id));
930
1720
  return [...uniqueAdded, ...modifiedRecords];
931
1721
  }, []);
932
- const [data, setData] = useState4(() => normalizeRecords(initialRecords));
933
- useEffect2(() => {
1722
+ const [data, setData] = useState5(() => normalizeRecords(initialRecords));
1723
+ useEffect4(() => {
934
1724
  if (initialRecords) {
935
1725
  setData(mergeWithDirty(normalizeRecords(initialRecords)));
936
1726
  }
@@ -945,54 +1735,83 @@ function TableBlockClient({
945
1735
  }
946
1736
  return false;
947
1737
  };
948
- const [baseRecordsMap, setBaseRecordsMap] = useState4(() => {
1738
+ const [baseRecordsMap, setBaseRecordsMap] = useState5(() => {
949
1739
  const map = /* @__PURE__ */ new Map();
950
1740
  (normalizeRecords(initialRecords) || []).forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
951
1741
  return map;
952
1742
  });
953
- useEffect2(() => {
1743
+ useEffect4(() => {
954
1744
  if (initialRecords && initialRecords.length > 0) {
955
1745
  const map = /* @__PURE__ */ new Map();
956
1746
  (normalizeRecords(initialRecords) || []).forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
957
1747
  setBaseRecordsMap(map);
958
1748
  }
959
1749
  }, [initialRecords]);
960
- useEffect2(() => {
1750
+ const refetchTableData = useCallback4((targetPage) => {
961
1751
  const tableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
962
- if (!tableId || !executeAction) return;
963
- if (!(schema == null ? void 0 : schema.columns) || schema.columns.length === 0 || (!initialRecords || initialRecords.length === 0)) {
964
- executeAction("FETCH_TABLE_DATA", { tableId }).then((res) => {
965
- var _a;
966
- if (res) {
967
- if (((_a = res.schema) == null ? void 0 : _a.columns) && res.schema.columns.length > 0) {
968
- setLocalColumns(res.schema.columns);
969
- }
970
- if (res.records && Array.isArray(res.records)) {
971
- const norm = normalizeRecords(res.records);
972
- const map = /* @__PURE__ */ new Map();
973
- norm.forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
974
- setBaseRecordsMap(map);
975
- setData(mergeWithDirty(norm));
1752
+ if (!tableId || !executeAction) return Promise.resolve();
1753
+ const activePage = targetPage !== void 0 ? targetPage : page;
1754
+ const skip = (activePage - 1) * limit;
1755
+ return executeAction("FETCH_TABLE_DATA", { tableId, page: activePage, limit, skip }).then((res) => {
1756
+ var _a;
1757
+ if (res) {
1758
+ if (((_a = res.schema) == null ? void 0 : _a.columns) && res.schema.columns.length > 0) {
1759
+ setLocalColumns((prev) => mergeColumnDefs(prev, res.schema.columns));
1760
+ }
1761
+ if (res.records && Array.isArray(res.records)) {
1762
+ const norm = normalizeRecords(res.records);
1763
+ const map = /* @__PURE__ */ new Map();
1764
+ norm.forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
1765
+ setBaseRecordsMap(map);
1766
+ setData(norm);
1767
+ }
1768
+ if (res.meta) {
1769
+ const total = typeof res.meta.totalRecords === "number" ? res.meta.totalRecords : 0;
1770
+ const pages = typeof res.meta.totalPages === "number" ? res.meta.totalPages : Math.ceil(total / limit) || 1;
1771
+ setTotalRecords(total);
1772
+ setTotalPages(pages);
1773
+ if (activePage > pages && pages >= 1) {
1774
+ setPage(pages);
976
1775
  }
1776
+ } else if (res.records && Array.isArray(res.records)) {
1777
+ setTotalRecords(res.records.length);
1778
+ setTotalPages(Math.ceil(res.records.length / limit) || 1);
977
1779
  }
978
- }).catch((err) => {
979
- console.error("[TableBlock] Failed to auto-fetch table data:", err);
980
- });
1780
+ }
1781
+ }).catch((err) => {
1782
+ console.error("[TableBlock] Failed to auto-fetch table data:", err);
1783
+ });
1784
+ }, [schema == null ? void 0 : schema.tableMetaId, schema == null ? void 0 : schema.tableId, schema == null ? void 0 : schema.id, page, limit, executeAction, mergeColumnDefs]);
1785
+ useEffect4(() => {
1786
+ refetchTableData();
1787
+ }, [refetchTableData]);
1788
+ const handleLimitChange = (newLimit) => {
1789
+ setLimit(newLimit);
1790
+ setPage(1);
1791
+ if (typeof window !== "undefined") {
1792
+ localStorage.setItem(`table_limit_${tableKey}`, String(newLimit));
981
1793
  }
982
- }, [schema == null ? void 0 : schema.tableMetaId, schema == null ? void 0 : schema.tableId, schema == null ? void 0 : schema.id, schema == null ? void 0 : schema.columns, initialRecords, executeAction, mergeWithDirty]);
983
- useEffect2(() => {
1794
+ };
1795
+ const handlePrevPage = () => {
1796
+ setPage((prev) => Math.max(1, prev - 1));
1797
+ };
1798
+ const handleNextPage = () => {
1799
+ setPage((prev) => Math.min(totalPages, prev + 1));
1800
+ };
1801
+ useEffect4(() => {
984
1802
  if (error) {
985
1803
  showError(error, "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E8\u05DB\u05D9\u05D1");
986
1804
  }
987
1805
  }, [error, showError]);
988
- const [searchQuery, setSearchQuery] = useState4("");
989
- const [selectedIds, setSelectedIds] = useState4(/* @__PURE__ */ new Set());
990
- const [pendingDeletions, setPendingDeletions] = useState4([]);
991
- const [isSaving, setIsSaving] = useState4(false);
992
- const [isSaveSuccess, setIsSaveSuccess] = useState4(false);
993
- const [relationOptions, setRelationOptions] = useState4({});
1806
+ const [searchQuery, setSearchQuery] = useState5("");
1807
+ const [selectedIds, setSelectedIds] = useState5(/* @__PURE__ */ new Set());
1808
+ const [pendingDeletions, setPendingDeletions] = useState5([]);
1809
+ const [isSaving, setIsSaving] = useState5(false);
1810
+ const [isSaveSuccess, setIsSaveSuccess] = useState5(false);
1811
+ const [relationOptions, setRelationOptions] = useState5({});
1812
+ const [expandedRowIds, setExpandedRowIds] = useState5(/* @__PURE__ */ new Set());
994
1813
  const schemaColsString = useMemo2(() => JSON.stringify(localColumns || []), [localColumns]);
995
- const refetchRelation = useCallback3(async (colName) => {
1814
+ const refetchRelation = useCallback4(async (colName) => {
996
1815
  var _a;
997
1816
  let parsedCols = [];
998
1817
  try {
@@ -1012,7 +1831,7 @@ function TableBlockClient({
1012
1831
  console.warn(`[TableBlock] Could not fetch relation options for column ${colName}:`, err);
1013
1832
  }
1014
1833
  }, [schemaColsString, executeAction]);
1015
- useEffect2(() => {
1834
+ useEffect4(() => {
1016
1835
  let parsedCols = [];
1017
1836
  try {
1018
1837
  parsedCols = JSON.parse(schemaColsString);
@@ -1026,10 +1845,144 @@ function TableBlockClient({
1026
1845
  refetchRelation(col.name);
1027
1846
  });
1028
1847
  }, [schemaColsString, refetchRelation]);
1848
+ const normalizedColumns = useMemo2(() => {
1849
+ return (localColumns || []).map((col, idx) => {
1850
+ const isRequired = col.isRequired === true || String(col.isRequired) === "true";
1851
+ const isUnique = col.isUnique === true || String(col.isUnique) === "true";
1852
+ const isProtected = isRequired || isUnique;
1853
+ return __spreadProps(__spreadValues({}, col), {
1854
+ order: typeof col.order === "number" ? col.order : idx,
1855
+ isHidden: isProtected ? false : Boolean(col.isHidden)
1856
+ });
1857
+ }).sort((a, b) => {
1858
+ var _a, _b;
1859
+ return ((_a = a.order) != null ? _a : 0) - ((_b = b.order) != null ? _b : 0);
1860
+ });
1861
+ }, [localColumns]);
1029
1862
  const filteredColumns = useMemo2(() => {
1030
1863
  const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
1031
- return (localColumns || []).filter((col) => !systemFields.includes(col.name));
1032
- }, [localColumns]);
1864
+ return normalizedColumns.filter((col) => !systemFields.includes(col.name) && !col.isHidden);
1865
+ }, [normalizedColumns]);
1866
+ const customizableColumns = useMemo2(() => {
1867
+ const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
1868
+ return normalizedColumns.filter((col) => !systemFields.includes(col.name));
1869
+ }, [normalizedColumns]);
1870
+ const [isColumnsDropdownOpen, setIsColumnsDropdownOpen] = useState5(false);
1871
+ const dropdownRef = useRef4(null);
1872
+ useEffect4(() => {
1873
+ const handleClickOutside = (e) => {
1874
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
1875
+ setIsColumnsDropdownOpen(false);
1876
+ }
1877
+ };
1878
+ if (isColumnsDropdownOpen) {
1879
+ document.addEventListener("mousedown", handleClickOutside);
1880
+ }
1881
+ return () => {
1882
+ document.removeEventListener("mousedown", handleClickOutside);
1883
+ };
1884
+ }, [isColumnsDropdownOpen]);
1885
+ const handleShowAllColumns = async () => {
1886
+ var _a;
1887
+ const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
1888
+ const systemCols = (localColumns || []).filter((c) => systemFields.includes(c.name));
1889
+ const updatedCustomCols = customizableColumns.map((col) => __spreadProps(__spreadValues({}, col), {
1890
+ isHidden: false
1891
+ }));
1892
+ const fullUpdatedColumns = [...systemCols, ...updatedCustomCols];
1893
+ setLocalColumns(fullUpdatedColumns);
1894
+ const targetTableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
1895
+ if (targetTableId && executeAction) {
1896
+ try {
1897
+ const res = await executeAction("UPDATE_TABLE_COLUMNS", {
1898
+ tableId: targetTableId,
1899
+ tableMetaId: targetTableId,
1900
+ columns: fullUpdatedColumns
1901
+ });
1902
+ const returnedCols = (res == null ? void 0 : res.columns) || ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.columns);
1903
+ if (returnedCols && Array.isArray(returnedCols)) {
1904
+ setLocalColumns((prev) => mergeColumnDefs(prev, returnedCols));
1905
+ }
1906
+ } catch (err) {
1907
+ console.error("[TableBlock] Failed to save show all columns:", err);
1908
+ showError((err == null ? void 0 : err.message) || String(err), "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D4\u05E6\u05D2\u05EA \u05DB\u05DC \u05D4\u05E2\u05DE\u05D5\u05D3\u05D5\u05EA");
1909
+ }
1910
+ }
1911
+ };
1912
+ const handleToggleColumnVisibility = async (colName, isVisible) => {
1913
+ var _a;
1914
+ const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
1915
+ const systemCols = (localColumns || []).filter((c) => systemFields.includes(c.name));
1916
+ const updatedCustomCols = customizableColumns.map((col) => {
1917
+ const isRequired = col.isRequired === true || String(col.isRequired) === "true";
1918
+ const isUnique = col.isUnique === true || String(col.isUnique) === "true";
1919
+ const isProtected = isRequired || isUnique;
1920
+ if (col.name === colName) {
1921
+ if (isProtected) {
1922
+ return __spreadProps(__spreadValues({}, col), { isHidden: false });
1923
+ }
1924
+ return __spreadProps(__spreadValues({}, col), { isHidden: !isVisible });
1925
+ }
1926
+ return __spreadProps(__spreadValues({}, col), {
1927
+ isHidden: isProtected ? false : Boolean(col.isHidden)
1928
+ });
1929
+ });
1930
+ const fullUpdatedColumns = [...systemCols, ...updatedCustomCols];
1931
+ setLocalColumns(fullUpdatedColumns);
1932
+ const targetTableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
1933
+ if (targetTableId && executeAction) {
1934
+ try {
1935
+ const res = await executeAction("UPDATE_TABLE_COLUMNS", {
1936
+ tableId: targetTableId,
1937
+ tableMetaId: targetTableId,
1938
+ columns: fullUpdatedColumns
1939
+ });
1940
+ const returnedCols = (res == null ? void 0 : res.columns) || ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.columns);
1941
+ if (returnedCols && Array.isArray(returnedCols)) {
1942
+ setLocalColumns((prev) => mergeColumnDefs(prev, returnedCols));
1943
+ }
1944
+ } catch (err) {
1945
+ console.error("[TableBlock] Failed to save column visibility:", err);
1946
+ showError((err == null ? void 0 : err.message) || String(err), "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E2\u05D3\u05DB\u05D5\u05DF \u05D4\u05E1\u05EA\u05E8\u05EA \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA");
1947
+ }
1948
+ }
1949
+ };
1950
+ const [draggedColName, setDraggedColName] = useState5(null);
1951
+ const [dragOverColName, setDragOverColName] = useState5(null);
1952
+ const handleColumnReorder = async (draggedName, dropTargetName) => {
1953
+ var _a;
1954
+ if (draggedName === dropTargetName) return;
1955
+ const currentCustomCols = [...customizableColumns];
1956
+ const fromIndex = currentCustomCols.findIndex((c) => c.name === draggedName);
1957
+ const toIndex = currentCustomCols.findIndex((c) => c.name === dropTargetName);
1958
+ if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return;
1959
+ const [draggedItem] = currentCustomCols.splice(fromIndex, 1);
1960
+ currentCustomCols.splice(toIndex, 0, draggedItem);
1961
+ const updatedCustomCols = currentCustomCols.map((col, idx) => __spreadProps(__spreadValues({}, col), {
1962
+ order: idx
1963
+ }));
1964
+ const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
1965
+ const systemCols = (localColumns || []).filter((c) => systemFields.includes(c.name));
1966
+ const fullUpdatedColumns = [...systemCols, ...updatedCustomCols];
1967
+ setLocalColumns(fullUpdatedColumns);
1968
+ const targetTableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
1969
+ if (targetTableId && executeAction) {
1970
+ try {
1971
+ const res = await executeAction("UPDATE_TABLE_COLUMNS", {
1972
+ tableId: targetTableId,
1973
+ tableMetaId: targetTableId,
1974
+ columns: fullUpdatedColumns
1975
+ });
1976
+ const returnedCols = (res == null ? void 0 : res.columns) || ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.columns);
1977
+ if (returnedCols && Array.isArray(returnedCols)) {
1978
+ setLocalColumns((prev) => mergeColumnDefs(prev, returnedCols));
1979
+ }
1980
+ } catch (err) {
1981
+ console.error("[TableBlock] Failed to save column reorder:", err);
1982
+ showError((err == null ? void 0 : err.message) || String(err), "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E2\u05D3\u05DB\u05D5\u05DF \u05E1\u05D3\u05E8 \u05D4\u05E2\u05DE\u05D5\u05D3\u05D5\u05EA");
1983
+ }
1984
+ }
1985
+ };
1033
1986
  const generateTempId = () => `temp_${Math.random().toString(36).substr(2, 9)}`;
1034
1987
  const handleAddRow = () => {
1035
1988
  const defaultData = {};
@@ -1037,6 +1990,10 @@ function TableBlockClient({
1037
1990
  var _a;
1038
1991
  const typeUpper = (_a = col.type) == null ? void 0 : _a.toUpperCase();
1039
1992
  let initialValue = col.defaultValue;
1993
+ if (typeUpper === "SUB_TABLE") {
1994
+ defaultData[col.name] = [];
1995
+ return;
1996
+ }
1040
1997
  if (initialValue !== void 0 && initialValue !== null && initialValue !== "") {
1041
1998
  if (typeUpper === "CURRENCY" || typeUpper === "NUMBER") {
1042
1999
  initialValue = parseFloat(initialValue);
@@ -1059,6 +2016,34 @@ function TableBlockClient({
1059
2016
  setData((prev) => [newRow, ...prev]);
1060
2017
  setDirty((prev) => __spreadProps(__spreadValues({}, prev), { added: [newRow, ...prev.added] }));
1061
2018
  };
2019
+ const handleDuplicateRow = () => {
2020
+ if (selectedIds.size !== 1) return;
2021
+ const targetId = Array.from(selectedIds)[0];
2022
+ const sourceRow = data.find((r) => r.id === targetId);
2023
+ if (!sourceRow) return;
2024
+ const uniqueColNames = new Set(
2025
+ (localColumns || []).filter((c) => c.isUnique === true || String(c.isUnique) === "true").map((c) => c.name)
2026
+ );
2027
+ const fieldsToScrub = /* @__PURE__ */ new Set(["id", "_id", "createdAt", "updatedAt", "created_at", "updated_at"]);
2028
+ const clonedData = __spreadValues({}, sourceRow.data);
2029
+ Object.keys(clonedData).forEach((key) => {
2030
+ var _a;
2031
+ if (fieldsToScrub.has(key) || uniqueColNames.has(key)) {
2032
+ const col = (localColumns || []).find((c) => c.name === key);
2033
+ const typeUpper = (_a = col == null ? void 0 : col.type) == null ? void 0 : _a.toUpperCase();
2034
+ if (typeUpper === "NUMBER" || typeUpper === "CURRENCY") {
2035
+ clonedData[key] = 0;
2036
+ } else if (typeUpper === "BOOLEAN") {
2037
+ clonedData[key] = false;
2038
+ } else {
2039
+ clonedData[key] = "";
2040
+ }
2041
+ }
2042
+ });
2043
+ const newRow = { id: generateTempId(), data: clonedData };
2044
+ setData((prev) => [newRow, ...prev]);
2045
+ setDirty((prev) => __spreadProps(__spreadValues({}, prev), { added: [newRow, ...prev.added] }));
2046
+ };
1062
2047
  const handleCellChange = (rowId, colName, value) => {
1063
2048
  setData((prev) => prev.map(
1064
2049
  (row) => row.id === rowId ? __spreadProps(__spreadValues({}, row), { data: __spreadProps(__spreadValues({}, row.data), { [colName]: value }) }) : row
@@ -1092,6 +2077,36 @@ function TableBlockClient({
1092
2077
  });
1093
2078
  }
1094
2079
  };
2080
+ const triggerSaveSuccess = useCallback4(() => {
2081
+ setIsSaveSuccess(true);
2082
+ setTimeout(() => {
2083
+ setIsSaveSuccess(false);
2084
+ }, 2500);
2085
+ }, []);
2086
+ const handleQuietCellChange = useCallback4((rowId, colName, value) => {
2087
+ setData((prev) => prev.map(
2088
+ (row) => row.id === rowId ? __spreadProps(__spreadValues({}, row), { data: __spreadProps(__spreadValues({}, row.data), { [colName]: value }) }) : row
2089
+ ));
2090
+ if (rowId.startsWith("temp_")) {
2091
+ setDirty((prev) => {
2092
+ if (!prev.added.some((r) => r.id === rowId)) return prev;
2093
+ return __spreadProps(__spreadValues({}, prev), {
2094
+ added: prev.added.map(
2095
+ (r) => r.id === rowId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value }) }) : r
2096
+ )
2097
+ });
2098
+ });
2099
+ } else {
2100
+ setBaseRecordsMap((prev) => {
2101
+ const next = new Map(prev);
2102
+ const rowData = next.get(rowId);
2103
+ if (rowData) {
2104
+ next.set(rowId, __spreadProps(__spreadValues({}, rowData), { [colName]: value }));
2105
+ }
2106
+ return next;
2107
+ });
2108
+ }
2109
+ }, []);
1095
2110
  const toggleSelectAll = (checked) => {
1096
2111
  const nextSelected = checked ? new Set(filteredData.map((r) => r.id)) : /* @__PURE__ */ new Set();
1097
2112
  setSelectedIds(nextSelected);
@@ -1121,9 +2136,15 @@ function TableBlockClient({
1121
2136
  const row = data.find((r) => r.id === id);
1122
2137
  if (row) {
1123
2138
  driveCols.forEach((col) => {
1124
- const fileObj = row.data[col.name];
1125
- if (fileObj && fileObj.fileId) {
1126
- filesToDelete.push(fileObj.fileId);
2139
+ const fileVal = row.data[col.name];
2140
+ if (Array.isArray(fileVal)) {
2141
+ fileVal.forEach((fileObj) => {
2142
+ if (fileObj && fileObj.fileId) {
2143
+ filesToDelete.push(fileObj.fileId);
2144
+ }
2145
+ });
2146
+ } else if (fileVal && fileVal.fileId) {
2147
+ filesToDelete.push(fileVal.fileId);
1127
2148
  }
1128
2149
  });
1129
2150
  }
@@ -1150,7 +2171,7 @@ function TableBlockClient({
1150
2171
  const formatPayload = (payload) => {
1151
2172
  const formatted = {};
1152
2173
  (localColumns || []).forEach((col) => {
1153
- var _a;
2174
+ var _a, _b;
1154
2175
  if (["_id", "id", "createdAt", "updatedAt", "orgId", "tableId", "isSelected"].includes(col.name)) return;
1155
2176
  let val = payload[col.name];
1156
2177
  if (val !== void 0) {
@@ -1160,6 +2181,11 @@ function TableBlockClient({
1160
2181
  if (((_a = col.type) == null ? void 0 : _a.toUpperCase()) === "NUMBER") {
1161
2182
  val = val === "" ? 0 : Number(val);
1162
2183
  if (isNaN(val)) val = 0;
2184
+ } else if (((_b = col.type) == null ? void 0 : _b.toUpperCase()) === "SUB_TABLE" && Array.isArray(val)) {
2185
+ val = val.map((item) => {
2186
+ const _a2 = item, { _resolvedData } = _a2, rest = __objRest(_a2, ["_resolvedData"]);
2187
+ return rest;
2188
+ });
1163
2189
  }
1164
2190
  formatted[col.name] = val;
1165
2191
  }
@@ -1279,6 +2305,7 @@ function TableBlockClient({
1279
2305
  return next;
1280
2306
  });
1281
2307
  setDirty({ added: [], modified: {}, deleted: [] });
2308
+ await refetchTableData();
1282
2309
  setIsSaveSuccess(true);
1283
2310
  setTimeout(() => {
1284
2311
  setIsSaveSuccess(false);
@@ -1302,32 +2329,60 @@ function TableBlockClient({
1302
2329
  }, [data, searchQuery]);
1303
2330
  const isUpdatingActive = Boolean(isUpdating || isSaving);
1304
2331
  const errorMessage = error ? typeof error === "string" ? error : error.message || String(error) : null;
1305
- return /* @__PURE__ */ jsxs5(
2332
+ const columnTotals = useMemo2(() => {
2333
+ const totals = {};
2334
+ if (!showSummary) return totals;
2335
+ filteredColumns.forEach((col) => {
2336
+ var _a;
2337
+ const typeUpper = (_a = col.type) == null ? void 0 : _a.toUpperCase();
2338
+ if (typeUpper === "CURRENCY" || typeUpper === "NUMBER") {
2339
+ const sum = filteredData.reduce((acc, row) => {
2340
+ var _a2;
2341
+ const val = (_a2 = row.data) == null ? void 0 : _a2[col.name];
2342
+ const num = Number(val);
2343
+ return acc + (isNaN(num) ? 0 : num);
2344
+ }, 0);
2345
+ totals[col.name] = sum;
2346
+ }
2347
+ });
2348
+ return totals;
2349
+ }, [filteredData, filteredColumns, showSummary]);
2350
+ return /* @__PURE__ */ jsxs6(
1306
2351
  "div",
1307
2352
  {
1308
2353
  id: componentId,
1309
2354
  "data-testid": dataTestId || (componentId ? `table-block-${componentId}` : void 0),
1310
- className: `flex flex-col w-full bg-white border border-slate-200 rounded-xl shadow-xs overflow-hidden ${className}`,
2355
+ className: `flex flex-col w-full bg-white border border-slate-200 rounded-xl shadow-xs min-h-[350px] overflow-hidden ${className}`,
1311
2356
  dir: "rtl",
1312
2357
  children: [
1313
- errorMessage && /* @__PURE__ */ jsx5("div", { className: "p-3 bg-rose-50 border-b border-rose-200 text-rose-700 text-sm flex items-center justify-between", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
1314
- /* @__PURE__ */ jsx5("svg", { className: "w-4 h-4 text-rose-500 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
1315
- /* @__PURE__ */ jsx5("span", { children: errorMessage })
2358
+ errorMessage && /* @__PURE__ */ jsx6("div", { className: "p-3 bg-rose-50 border-b border-rose-200 text-rose-700 text-sm flex items-center justify-between", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
2359
+ /* @__PURE__ */ jsx6("svg", { className: "w-4 h-4 text-rose-500 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
2360
+ /* @__PURE__ */ jsx6("span", { children: errorMessage })
1316
2361
  ] }) }),
1317
- /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center justify-between gap-3 p-3.5 sm:p-4 bg-slate-50 border-b border-slate-200", children: [
1318
- /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-2.5", children: [
1319
- ((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name)) && /* @__PURE__ */ jsx5("div", { className: "font-bold text-slate-800 text-sm sm:text-base border-l border-slate-300 pl-3 ml-1", children: /* @__PURE__ */ jsx5("span", { children: schema.title || schema.name }) }),
1320
- /* @__PURE__ */ jsx5(
2362
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center justify-between gap-3 p-3.5 sm:p-4 bg-slate-50 border-b border-slate-200", children: [
2363
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-2.5", children: [
2364
+ ((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name)) && /* @__PURE__ */ jsx6("div", { className: "font-bold text-slate-800 text-sm sm:text-base border-l border-slate-300 pl-3 ml-1", children: /* @__PURE__ */ jsx6("span", { children: schema.title || schema.name }) }),
2365
+ /* @__PURE__ */ jsx6(
1321
2366
  "button",
1322
2367
  {
1323
2368
  onClick: handleAddRow,
1324
2369
  disabled: isLoading || isUpdatingActive,
1325
2370
  className: "w-9 h-9 bg-emerald-600 hover:bg-emerald-700 active:bg-emerald-800 text-white rounded-lg shadow-xs flex items-center justify-center transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer",
1326
2371
  title: "\u05D4\u05D5\u05E1\u05E3 \u05E9\u05D5\u05E8\u05D4 \u05D7\u05D3\u05E9\u05D4",
1327
- children: /* @__PURE__ */ jsx5("svg", { className: "w-4.5 h-4.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M12 4v16m8-8H4" }) })
2372
+ children: /* @__PURE__ */ jsx6("svg", { className: "w-4.5 h-4.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M12 4v16m8-8H4" }) })
1328
2373
  }
1329
2374
  ),
1330
- /* @__PURE__ */ jsxs5(
2375
+ /* @__PURE__ */ jsx6(
2376
+ "button",
2377
+ {
2378
+ onClick: handleDuplicateRow,
2379
+ disabled: isLoading || isUpdatingActive || selectedIds.size !== 1,
2380
+ className: "w-9 h-9 bg-blue-50 hover:bg-blue-100 border border-blue-200 text-blue-700 rounded-lg shadow-2xs flex items-center justify-center transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer",
2381
+ title: "\u05E9\u05DB\u05E4\u05DC \u05E9\u05D5\u05E8\u05D4 \u05E0\u05D1\u05D7\u05E8\u05EA",
2382
+ children: /* @__PURE__ */ jsx6(Copy2, { className: "w-4 h-4 text-blue-600 shrink-0" })
2383
+ }
2384
+ ),
2385
+ /* @__PURE__ */ jsxs6(
1331
2386
  "button",
1332
2387
  {
1333
2388
  onClick: handleDeleteSelected,
@@ -1335,14 +2390,120 @@ function TableBlockClient({
1335
2390
  className: "w-9 h-9 bg-rose-50 hover:bg-rose-100 border border-rose-200 text-rose-700 rounded-lg shadow-2xs flex items-center justify-center relative transition-all disabled:opacity-40 disabled:pointer-events-none cursor-pointer",
1336
2391
  title: "\u05DE\u05D7\u05E7 \u05E9\u05D5\u05E8\u05D5\u05EA \u05E0\u05D1\u05D7\u05E8\u05D5\u05EA",
1337
2392
  children: [
1338
- /* @__PURE__ */ jsx5("svg", { className: "w-4.5 h-4.5 text-rose-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }),
1339
- selectedIds.size > 0 && /* @__PURE__ */ jsx5("span", { className: "absolute -top-1.5 -right-1.5 bg-rose-600 text-white min-w-[18px] h-[18px] px-1 rounded-full text-[10px] font-bold flex items-center justify-center shadow-xs", children: selectedIds.size })
2393
+ /* @__PURE__ */ jsx6("svg", { className: "w-4.5 h-4.5 text-rose-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }),
2394
+ selectedIds.size > 0 && /* @__PURE__ */ jsx6("span", { className: "absolute -top-1.5 -right-1.5 bg-rose-600 text-white min-w-[18px] h-[18px] px-1 rounded-full text-[10px] font-bold flex items-center justify-center shadow-xs", children: selectedIds.size })
1340
2395
  ]
1341
2396
  }
1342
2397
  ),
1343
- /* @__PURE__ */ jsx5("div", { className: "h-5 w-px bg-slate-300 mx-1 hidden sm:block" }),
1344
- /* @__PURE__ */ jsxs5("div", { className: "relative flex-1 sm:w-64 max-w-xs", children: [
1345
- /* @__PURE__ */ jsx5(
2398
+ /* @__PURE__ */ jsxs6(
2399
+ "button",
2400
+ {
2401
+ onClick: toggleSummary,
2402
+ disabled: isLoading || isUpdatingActive,
2403
+ className: `h-9 px-3 border 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 ${showSummary ? "bg-indigo-50 border-indigo-200 text-indigo-700 hover:bg-indigo-100" : "bg-white border-slate-300 text-slate-700 hover:bg-slate-100"}`,
2404
+ title: "\u05D4\u05E6\u05D2/\u05D4\u05E1\u05EA\u05E8 \u05E9\u05D5\u05E8\u05EA \u05E1\u05D9\u05DB\u05D5\u05DD",
2405
+ children: [
2406
+ /* @__PURE__ */ jsx6("svg", { className: "w-4 h-4 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M4 6h16M4 10h16M4 14h16M4 18h16" }) }),
2407
+ /* @__PURE__ */ jsx6("span", { className: "hidden sm:inline", children: "\u05E1\u05DB\u05D9\u05DE\u05D4" })
2408
+ ]
2409
+ }
2410
+ ),
2411
+ /* @__PURE__ */ jsxs6("div", { className: "relative", ref: dropdownRef, children: [
2412
+ /* @__PURE__ */ jsxs6(
2413
+ "button",
2414
+ {
2415
+ onClick: () => setIsColumnsDropdownOpen((prev) => !prev),
2416
+ disabled: isLoading || isUpdatingActive,
2417
+ className: "h-9 px-3 bg-white hover:bg-slate-100 border border-slate-300 text-slate-700 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",
2418
+ title: "\u05D4\u05E6\u05D2/\u05D4\u05E1\u05EA\u05E8 \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA",
2419
+ children: [
2420
+ /* @__PURE__ */ jsx6("svg", { className: "w-4 h-4 text-slate-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" }) }),
2421
+ /* @__PURE__ */ jsx6("svg", { className: `w-3.5 h-3.5 text-slate-500 transition-transform ${isColumnsDropdownOpen ? "rotate-180" : ""}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M19 9l-7 7-7-7" }) })
2422
+ ]
2423
+ }
2424
+ ),
2425
+ isColumnsDropdownOpen && /* @__PURE__ */ jsxs6("div", { className: "absolute right-0 top-full mt-1.5 w-56 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", children: [
2426
+ /* @__PURE__ */ jsxs6("div", { className: "px-2 py-1.5 text-xs font-bold text-slate-500 border-b border-slate-100 mb-1 flex items-center justify-between", children: [
2427
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5", children: [
2428
+ /* @__PURE__ */ jsx6("span", { children: "\u05D4\u05E6\u05D2\u05EA \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA" }),
2429
+ /* @__PURE__ */ jsxs6("span", { className: "text-[10px] font-normal text-slate-400", children: [
2430
+ "(",
2431
+ customizableColumns.filter((c) => !c.isHidden).length,
2432
+ "/",
2433
+ customizableColumns.length,
2434
+ ")"
2435
+ ] })
2436
+ ] }),
2437
+ /* @__PURE__ */ jsx6(
2438
+ "button",
2439
+ {
2440
+ type: "button",
2441
+ onClick: handleShowAllColumns,
2442
+ className: "text-xs text-brand hover:text-brand-hover font-semibold hover:underline cursor-pointer",
2443
+ children: "\u05D4\u05E6\u05D2 \u05D4\u05DB\u05DC"
2444
+ }
2445
+ )
2446
+ ] }),
2447
+ /* @__PURE__ */ jsx6("div", { className: "max-h-60 overflow-y-auto space-y-0.5", children: customizableColumns.map((col) => {
2448
+ const isRequired = col.isRequired === true || String(col.isRequired) === "true";
2449
+ const isUnique = col.isUnique === true || String(col.isUnique) === "true";
2450
+ const isProtected = isRequired || isUnique;
2451
+ const isVisible = !col.isHidden;
2452
+ let protectedExplanation = "";
2453
+ if (isRequired && isUnique) {
2454
+ protectedExplanation = "\u05E9\u05D3\u05D4 \u05D7\u05D5\u05D1\u05D4 \u05D5\u05D9\u05D9\u05D7\u05D5\u05D3\u05D9 - \u05DC\u05D0 \u05E0\u05D9\u05EA\u05DF \u05DC\u05D4\u05D5\u05E8\u05D9\u05D3 \u05E2\u05DE\u05D5\u05D3\u05D4 \u05D6\u05D5 \u05DE\u05D4\u05EA\u05E6\u05D5\u05D2\u05D4";
2455
+ } else if (isRequired) {
2456
+ protectedExplanation = "\u05E9\u05D3\u05D4 \u05D7\u05D5\u05D1\u05D4 - \u05DC\u05D0 \u05E0\u05D9\u05EA\u05DF \u05DC\u05D4\u05D5\u05E8\u05D9\u05D3 \u05E2\u05DE\u05D5\u05D3\u05D4 \u05D6\u05D5 \u05DE\u05D4\u05EA\u05E6\u05D5\u05D2\u05D4";
2457
+ } else if (isUnique) {
2458
+ protectedExplanation = "\u05E9\u05D3\u05D4 \u05D9\u05D9\u05D7\u05D5\u05D3\u05D9 - \u05DC\u05D0 \u05E0\u05D9\u05EA\u05DF \u05DC\u05D4\u05D5\u05E8\u05D9\u05D3 \u05E2\u05DE\u05D5\u05D3\u05D4 \u05D6\u05D5 \u05DE\u05D4\u05EA\u05E6\u05D5\u05D2\u05D4";
2459
+ }
2460
+ const badgeLabel = isRequired && isUnique ? "\u05D7\u05D5\u05D1\u05D4 + \u05D9\u05D9\u05D7\u05D5\u05D3\u05D9" : isRequired ? "\u05E9\u05D3\u05D4 \u05D7\u05D5\u05D1\u05D4" : "\u05E9\u05D3\u05D4 \u05D9\u05D9\u05D7\u05D5\u05D3\u05D9";
2461
+ return /* @__PURE__ */ jsxs6(
2462
+ "div",
2463
+ {
2464
+ onClick: () => {
2465
+ if (!isProtected) {
2466
+ handleToggleColumnVisibility(col.name, !isVisible);
2467
+ }
2468
+ },
2469
+ title: isProtected ? protectedExplanation : void 0,
2470
+ className: `group flex items-center justify-between px-2.5 py-1.5 rounded-lg text-xs transition-colors select-none ${isProtected ? "opacity-75 bg-slate-50 border border-slate-100/70 cursor-not-allowed" : "hover:bg-slate-100 active:bg-slate-200 cursor-pointer"}`,
2471
+ children: [
2472
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 min-w-0", children: [
2473
+ /* @__PURE__ */ jsx6(
2474
+ "input",
2475
+ {
2476
+ type: "checkbox",
2477
+ checked: isVisible,
2478
+ disabled: isProtected,
2479
+ onChange: () => {
2480
+ },
2481
+ className: "w-3.5 h-3.5 rounded border-slate-300 text-brand focus:ring-brand/20 cursor-pointer disabled:cursor-not-allowed disabled:opacity-60"
2482
+ }
2483
+ ),
2484
+ /* @__PURE__ */ jsx6("span", { className: `font-medium truncate ${isProtected ? "text-slate-500" : "text-slate-700"}`, children: col.name })
2485
+ ] }),
2486
+ isProtected && /* @__PURE__ */ jsxs6(
2487
+ "div",
2488
+ {
2489
+ className: "flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-semibold bg-amber-50 text-amber-800 border border-amber-200/80 cursor-help",
2490
+ title: protectedExplanation,
2491
+ children: [
2492
+ /* @__PURE__ */ jsx6("svg", { className: "w-3 h-3 text-amber-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: "2.2", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" }) }),
2493
+ /* @__PURE__ */ jsx6("span", { children: badgeLabel })
2494
+ ]
2495
+ }
2496
+ )
2497
+ ]
2498
+ },
2499
+ col.name
2500
+ );
2501
+ }) })
2502
+ ] })
2503
+ ] }),
2504
+ /* @__PURE__ */ jsx6("div", { className: "h-5 w-px bg-slate-300 mx-1 hidden sm:block" }),
2505
+ /* @__PURE__ */ jsxs6("div", { className: "relative flex-1 sm:w-64 max-w-xs", children: [
2506
+ /* @__PURE__ */ jsx6(
1346
2507
  "input",
1347
2508
  {
1348
2509
  placeholder: "\u05D7\u05D9\u05E4\u05D5\u05E9 \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA...",
@@ -1352,36 +2513,45 @@ function TableBlockClient({
1352
2513
  className: "w-full h-9 px-3 pr-9 text-xs border border-slate-300 rounded-lg bg-white shadow-2xs focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 text-slate-800 disabled:opacity-50"
1353
2514
  }
1354
2515
  ),
1355
- /* @__PURE__ */ jsx5("svg", { className: "w-4 h-4 text-slate-400 absolute right-2.5 top-2.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) })
2516
+ searchQuery ? /* @__PURE__ */ jsx6(
2517
+ "button",
2518
+ {
2519
+ type: "button",
2520
+ onClick: () => setSearchQuery(""),
2521
+ className: "absolute right-2.5 top-2.5 text-slate-400 hover:text-slate-600 transition-colors p-0.5 rounded-full hover:bg-slate-100 cursor-pointer",
2522
+ title: "Clear search",
2523
+ children: /* @__PURE__ */ jsx6(X4, { className: "w-3.5 h-3.5" })
2524
+ }
2525
+ ) : /* @__PURE__ */ jsx6("svg", { className: "w-4 h-4 text-slate-400 absolute right-2.5 top-2.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) })
1356
2526
  ] })
1357
2527
  ] }),
1358
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3", children: [
1359
- isUpdatingActive ? /* @__PURE__ */ jsxs5("span", { className: "text-xs text-blue-600 font-semibold flex items-center gap-1.5 bg-blue-50 border border-blue-200 px-3 py-1.5 rounded-lg animate-pulse", children: [
1360
- /* @__PURE__ */ jsx5("span", { className: "w-2 h-2 rounded-full bg-blue-500 inline-block" }),
2528
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3", children: [
2529
+ isUpdatingActive ? /* @__PURE__ */ jsxs6("span", { className: "text-xs text-blue-600 font-semibold flex items-center gap-1.5 bg-blue-50 border border-blue-200 px-3 py-1.5 rounded-lg animate-pulse", children: [
2530
+ /* @__PURE__ */ jsx6("span", { className: "w-2 h-2 rounded-full bg-blue-500 inline-block" }),
1361
2531
  "\u05DE\u05E2\u05D3\u05DB\u05DF..."
1362
- ] }) : isSaveSuccess ? /* @__PURE__ */ jsxs5("span", { className: "text-xs text-emerald-700 font-semibold flex items-center gap-1.5 bg-emerald-50 border border-emerald-200 px-3 py-1.5 rounded-lg animate-in fade-in duration-200", children: [
1363
- /* @__PURE__ */ jsx5("svg", { className: "w-4 h-4 text-emerald-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M5 13l4 4L19 7" }) }),
2532
+ ] }) : isSaveSuccess ? /* @__PURE__ */ jsxs6("span", { className: "text-xs text-emerald-700 font-semibold flex items-center gap-1.5 bg-emerald-50 border border-emerald-200 px-3 py-1.5 rounded-lg animate-in fade-in duration-200", children: [
2533
+ /* @__PURE__ */ jsx6("svg", { className: "w-4 h-4 text-emerald-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M5 13l4 4L19 7" }) }),
1364
2534
  "\u05E2\u05D5\u05D3\u05DB\u05DF \u05D1\u05D4\u05E6\u05DC\u05D7\u05D4"
1365
- ] }) : hasChanges ? /* @__PURE__ */ jsx5("span", { className: "text-xs font-semibold text-amber-700 animate-in fade-in", children: "\u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD \u05E9\u05DC\u05D0 \u05E0\u05E9\u05DE\u05E8\u05D5" }) : null,
1366
- /* @__PURE__ */ jsx5(
2535
+ ] }) : hasChanges ? /* @__PURE__ */ jsx6("span", { className: "text-xs font-semibold text-amber-700 animate-in fade-in", children: "\u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD \u05E9\u05DC\u05D0 \u05E0\u05E9\u05DE\u05E8\u05D5" }) : null,
2536
+ /* @__PURE__ */ jsx6(
1367
2537
  "button",
1368
2538
  {
1369
2539
  onClick: handleSave,
1370
2540
  disabled: isUpdatingActive || isLoading || !hasChanges,
1371
2541
  className: `h-9 px-4 text-xs font-bold rounded-lg shadow-2xs transition-all flex items-center justify-center cursor-pointer ${hasChanges && !isUpdatingActive && !isLoading ? "bg-brand hover:bg-brand-hover active:bg-[#00897b] text-white shadow-teal-500/20" : "opacity-50 grayscale bg-slate-200 text-slate-400 cursor-not-allowed"}`,
1372
- children: /* @__PURE__ */ jsx5("span", { children: isUpdatingActive ? "\u05E9\u05D5\u05DE\u05E8..." : "\u05E9\u05DE\u05D9\u05E8\u05EA \u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD" })
2542
+ children: /* @__PURE__ */ jsx6("span", { children: isUpdatingActive ? "\u05E9\u05D5\u05DE\u05E8..." : "\u05E9\u05DE\u05D9\u05E8\u05EA \u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD" })
1373
2543
  }
1374
2544
  )
1375
2545
  ] })
1376
2546
  ] }),
1377
- /* @__PURE__ */ jsxs5("div", { className: "overflow-x-auto relative", children: [
1378
- isLoading && /* @__PURE__ */ jsxs5("div", { className: "absolute inset-0 bg-white/80 backdrop-blur-xs z-20 flex flex-col items-center justify-center gap-2", children: [
1379
- /* @__PURE__ */ jsx5("div", { className: "w-6 h-6 border-2 border-brand border-t-transparent rounded-full animate-spin" }),
1380
- /* @__PURE__ */ jsx5("span", { className: "text-sm font-medium text-slate-600", children: "\u05D8\u05D5\u05E2\u05DF \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD..." })
2547
+ /* @__PURE__ */ jsxs6("div", { className: `overflow-x-auto relative min-h-[260px] flex-1 transition-opacity duration-200 ${isMounted ? "opacity-100" : "opacity-0"}`, children: [
2548
+ isLoading && /* @__PURE__ */ jsxs6("div", { className: "absolute inset-0 bg-white/80 backdrop-blur-xs z-20 flex flex-col items-center justify-center gap-2", children: [
2549
+ /* @__PURE__ */ jsx6("div", { className: "w-6 h-6 border-2 border-brand border-t-transparent rounded-full animate-spin" }),
2550
+ /* @__PURE__ */ jsx6("span", { className: "text-sm font-medium text-slate-600", children: "\u05D8\u05D5\u05E2\u05DF \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD..." })
1381
2551
  ] }),
1382
- /* @__PURE__ */ jsxs5("table", { className: "w-full text-sm text-right border-collapse", children: [
1383
- /* @__PURE__ */ jsx5("thead", { className: "bg-slate-100/80 text-slate-700 sticky top-0 z-10", children: /* @__PURE__ */ jsxs5("tr", { children: [
1384
- /* @__PURE__ */ jsx5("th", { className: "border-b border-l border-slate-200 p-2.5 w-10 text-center", children: /* @__PURE__ */ jsx5(
2552
+ /* @__PURE__ */ jsxs6("table", { className: "w-full text-sm text-right border-collapse table-auto", children: [
2553
+ /* @__PURE__ */ jsx6("thead", { className: "bg-slate-100/80 text-slate-700 sticky top-0 z-10", children: /* @__PURE__ */ jsxs6("tr", { children: [
2554
+ /* @__PURE__ */ jsx6("th", { className: "border-b border-l border-slate-200 p-2.5 w-10 text-center shrink-0", style: { width: 50, minWidth: 50, maxWidth: 50 }, children: /* @__PURE__ */ jsx6(
1385
2555
  "input",
1386
2556
  {
1387
2557
  type: "checkbox",
@@ -1391,67 +2561,313 @@ function TableBlockClient({
1391
2561
  className: "w-4 h-4 rounded border-slate-300 text-brand"
1392
2562
  }
1393
2563
  ) }),
1394
- filteredColumns.map((col) => /* @__PURE__ */ jsx5("th", { className: "border-b border-l border-slate-200 px-4 py-2.5 font-semibold text-slate-700 whitespace-nowrap min-w-[140px]", children: col.name }, col.name))
1395
- ] }) }),
1396
- /* @__PURE__ */ jsx5("tbody", { className: "bg-white", children: (isStateEmpty || filteredData.length === 0) && !isLoading ? /* @__PURE__ */ jsx5("tr", { children: /* @__PURE__ */ jsx5("td", { colSpan: filteredColumns.length + 1, className: "text-center py-12 text-slate-400", children: /* @__PURE__ */ jsxs5("div", { className: "flex flex-col items-center justify-center gap-2", children: [
1397
- /* @__PURE__ */ jsx5("svg", { className: "w-8 h-8 text-slate-300", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5", d: "M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" }) }),
1398
- /* @__PURE__ */ jsx5("span", { children: "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05DC\u05D4\u05E6\u05D2\u05D4." })
1399
- ] }) }) }) : filteredData.map((row) => /* @__PURE__ */ jsxs5("tr", { className: "hover:bg-blue-50/30 transition-colors group", children: [
1400
- /* @__PURE__ */ jsx5("td", { className: "border-b border-l border-slate-200 p-1.5 text-center bg-white group-hover:bg-transparent", children: /* @__PURE__ */ jsx5(
1401
- "input",
1402
- {
1403
- type: "checkbox",
1404
- checked: selectedIds.has(row.id),
1405
- onChange: (e) => toggleSelectRow(row.id, e.target.checked),
1406
- disabled: isLoading || isUpdatingActive,
1407
- className: "w-4 h-4 rounded border-slate-300 text-brand"
1408
- }
1409
- ) }),
1410
2564
  filteredColumns.map((col) => {
2565
+ const isDragging = draggedColName === col.name;
2566
+ const isDragOver = dragOverColName === col.name;
2567
+ const currentWidth = columnWidths[col.name] || 200;
2568
+ return /* @__PURE__ */ jsxs6(
2569
+ "th",
2570
+ {
2571
+ style: { width: currentWidth, minWidth: currentWidth, maxWidth: currentWidth },
2572
+ draggable: !isLoading && !isUpdatingActive,
2573
+ onDragStart: (e) => {
2574
+ e.dataTransfer.setData("text/plain", col.name);
2575
+ e.dataTransfer.effectAllowed = "move";
2576
+ setDraggedColName(col.name);
2577
+ },
2578
+ onDragOver: (e) => {
2579
+ e.preventDefault();
2580
+ e.dataTransfer.dropEffect = "move";
2581
+ if (dragOverColName !== col.name) {
2582
+ setDragOverColName(col.name);
2583
+ }
2584
+ },
2585
+ onDragLeave: () => {
2586
+ if (dragOverColName === col.name) {
2587
+ setDragOverColName(null);
2588
+ }
2589
+ },
2590
+ onDrop: (e) => {
2591
+ e.preventDefault();
2592
+ if (draggedColName && draggedColName !== col.name) {
2593
+ handleColumnReorder(draggedColName, col.name);
2594
+ }
2595
+ setDraggedColName(null);
2596
+ setDragOverColName(null);
2597
+ },
2598
+ onDragEnd: () => {
2599
+ setDraggedColName(null);
2600
+ setDragOverColName(null);
2601
+ },
2602
+ className: `relative border-b border-l border-slate-200 px-4 py-2.5 font-semibold text-slate-700 whitespace-nowrap select-none cursor-grab active:cursor-grabbing transition-colors ${isDragging ? "opacity-40 bg-slate-200 border-dashed border-2 border-brand" : ""} ${isDragOver ? "bg-blue-50 border-r-4 border-r-brand text-brand font-bold" : "hover:bg-slate-200/60"}`,
2603
+ children: [
2604
+ /* @__PURE__ */ jsx6(
2605
+ "div",
2606
+ {
2607
+ onMouseDown: (e) => {
2608
+ e.preventDefault();
2609
+ e.stopPropagation();
2610
+ setResizingCol({ name: col.name, startWidth: currentWidth, startX: e.clientX });
2611
+ },
2612
+ className: "absolute left-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-brand active:bg-brand-hover z-20",
2613
+ title: "\u05E9\u05E0\u05D4 \u05D2\u05D5\u05D3\u05DC \u05E2\u05DE\u05D5\u05D3\u05D4"
2614
+ }
2615
+ ),
2616
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between gap-2 group overflow-hidden", title: "\u05D2\u05E8\u05D5\u05E8 \u05DC\u05E9\u05D9\u05E0\u05D5\u05D9 \u05E1\u05D3\u05E8 \u05D4\u05E2\u05DE\u05D5\u05D3\u05D4", children: [
2617
+ /* @__PURE__ */ jsx6("span", { className: "truncate", children: col.name }),
2618
+ /* @__PURE__ */ jsx6(
2619
+ "svg",
2620
+ {
2621
+ className: "w-3.5 h-3.5 text-slate-400 opacity-40 group-hover:opacity-100 transition-opacity shrink-0 cursor-grab",
2622
+ fill: "none",
2623
+ viewBox: "0 0 24 24",
2624
+ stroke: "currentColor",
2625
+ children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M8 9h.01M8 15h.01M16 9h.01M16 15h.01" })
2626
+ }
2627
+ )
2628
+ ] })
2629
+ ]
2630
+ },
2631
+ col.name
2632
+ );
2633
+ }),
2634
+ /* @__PURE__ */ jsx6("th", { className: "w-full min-w-[50px] border-b border-slate-200 pointer-events-none" })
2635
+ ] }) }),
2636
+ /* @__PURE__ */ jsx6("tbody", { className: "bg-white", children: (isStateEmpty || filteredData.length === 0) && !isLoading ? /* @__PURE__ */ jsx6("tr", { children: /* @__PURE__ */ jsx6("td", { colSpan: filteredColumns.length + 2, className: "text-center py-12 text-slate-400", children: /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center justify-center gap-2", children: [
2637
+ /* @__PURE__ */ jsx6("svg", { className: "w-8 h-8 text-slate-300", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5", d: "M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" }) }),
2638
+ /* @__PURE__ */ jsx6("span", { children: "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05DC\u05D4\u05E6\u05D2\u05D4." })
2639
+ ] }) }) }) : filteredData.map((row) => {
2640
+ const hasSubTableCols = filteredColumns.some((c) => {
1411
2641
  var _a;
1412
- const colType = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
1413
- const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
1414
- let value = (row == null ? void 0 : row.data) ? row.data[col.name] : void 0;
1415
- if (value === void 0 && col.defaultValue !== void 0) {
1416
- value = colType === "CURRENCY" || colType === "NUMBER" ? parseFloat(col.defaultValue) : col.defaultValue;
1417
- if (colType === "CURRENCY" || colType === "NUMBER") {
1418
- if (isNaN(value)) value = null;
1419
- }
1420
- }
1421
- if (value === void 0) value = null;
1422
- return /* @__PURE__ */ jsx5("td", { className: "border-b border-l border-slate-200 p-0 relative", children: /* @__PURE__ */ jsx5(
1423
- InputComponent,
2642
+ return ((_a = c.type) == null ? void 0 : _a.toUpperCase()) === "SUB_TABLE";
2643
+ });
2644
+ const isExpanded = expandedRowIds.has(row.id);
2645
+ const subTableColumns = filteredColumns.filter((c) => {
2646
+ var _a;
2647
+ return ((_a = c.type) == null ? void 0 : _a.toUpperCase()) === "SUB_TABLE";
2648
+ });
2649
+ return /* @__PURE__ */ jsxs6(React6.Fragment, { children: [
2650
+ /* @__PURE__ */ jsxs6("tr", { className: "hover:bg-blue-50/30 transition-colors group", children: [
2651
+ /* @__PURE__ */ jsx6("td", { className: "border-b border-l border-slate-200 p-1.5 text-center bg-white group-hover:bg-transparent", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-center gap-1", children: [
2652
+ hasSubTableCols && /* @__PURE__ */ jsx6(
2653
+ "button",
2654
+ {
2655
+ type: "button",
2656
+ onClick: () => setExpandedRowIds((prev) => {
2657
+ const next = new Set(prev);
2658
+ if (next.has(row.id)) next.delete(row.id);
2659
+ else next.add(row.id);
2660
+ return next;
2661
+ }),
2662
+ className: "text-slate-400 hover:text-blue-600 p-0.5 rounded transition-colors cursor-pointer",
2663
+ title: isExpanded ? "\u05E1\u05D2\u05D5\u05E8 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4" : "\u05E4\u05EA\u05D7 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4",
2664
+ children: /* @__PURE__ */ jsx6("svg", { className: `w-3.5 h-3.5 transition-transform duration-150 ${isExpanded ? "rotate-90" : ""}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M9 5l7 7-7 7" }) })
2665
+ }
2666
+ ),
2667
+ /* @__PURE__ */ jsx6(
2668
+ "input",
2669
+ {
2670
+ type: "checkbox",
2671
+ checked: selectedIds.has(row.id),
2672
+ onChange: (e) => toggleSelectRow(row.id, e.target.checked),
2673
+ disabled: isLoading || isUpdatingActive,
2674
+ className: "w-4 h-4 rounded border-slate-300 text-brand"
2675
+ }
2676
+ )
2677
+ ] }) }),
2678
+ filteredColumns.map((col) => {
2679
+ var _a;
2680
+ const colType = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
2681
+ const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
2682
+ let value = (row == null ? void 0 : row.data) ? row.data[col.name] : void 0;
2683
+ if (value === void 0 && col.defaultValue !== void 0) {
2684
+ value = colType === "CURRENCY" || colType === "NUMBER" ? parseFloat(col.defaultValue) : col.defaultValue;
2685
+ if (colType === "CURRENCY" || colType === "NUMBER") {
2686
+ if (isNaN(value)) value = null;
2687
+ }
2688
+ }
2689
+ if (value === void 0) value = null;
2690
+ return /* @__PURE__ */ jsx6("td", { className: "border-b border-l border-slate-200 p-0 relative", children: /* @__PURE__ */ jsx6(
2691
+ InputComponent,
2692
+ {
2693
+ value,
2694
+ column: col,
2695
+ rowId: row.id,
2696
+ onChange: (v) => handleCellChange(row.id, col.name, v),
2697
+ context: {
2698
+ tableName: (schema == null ? void 0 : schema.name) || (schema == null ? void 0 : schema.tableName) || (schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.id) || "Table",
2699
+ tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
2700
+ onMarkForDeletion: (fileId) => setPendingDeletions((prev) => [...prev, fileId]),
2701
+ relationOptions,
2702
+ refetchRelation,
2703
+ executeAction,
2704
+ emitEvent,
2705
+ pageState,
2706
+ componentId,
2707
+ orgId,
2708
+ updateQuietly: handleQuietCellChange,
2709
+ triggerSaveSuccess,
2710
+ rowData: row.data
2711
+ }
2712
+ }
2713
+ ) }, col.name);
2714
+ }),
2715
+ /* @__PURE__ */ jsx6("td", { className: "border-b border-slate-200 bg-white pointer-events-none" })
2716
+ ] }),
2717
+ isExpanded && /* @__PURE__ */ jsx6(
2718
+ ExpandedSubTables,
1424
2719
  {
1425
- value,
1426
- column: col,
1427
2720
  rowId: row.id,
1428
- onChange: (v) => handleCellChange(row.id, col.name, v),
2721
+ rowData: row.data,
2722
+ subTableColumns,
2723
+ filteredColumnsLength: filteredColumns.length,
2724
+ handleCellChange,
1429
2725
  context: {
1430
- tableName: (schema == null ? void 0 : schema.name) || (schema == null ? void 0 : schema.tableName) || (schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.id) || "Table",
1431
- onMarkForDeletion: (fileId) => setPendingDeletions((prev) => [...prev, fileId]),
1432
- relationOptions,
1433
- refetchRelation,
1434
2726
  executeAction,
1435
- emitEvent,
1436
- pageState,
1437
- componentId,
1438
- orgId
2727
+ orgId,
2728
+ parentTableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
2729
+ tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
2730
+ rowData: row.data,
2731
+ updateQuietly: handleQuietCellChange,
2732
+ triggerSaveSuccess
1439
2733
  }
1440
2734
  }
1441
- ) }, col.name);
1442
- })
1443
- ] }, row.id)) })
2735
+ )
2736
+ ] }, row.id);
2737
+ }) }),
2738
+ showSummary && filteredData.length > 0 && /* @__PURE__ */ jsx6("tfoot", { className: "bg-slate-50 border-t-2 border-slate-300 sticky bottom-0 z-10 shadow-[0_-2px_4px_rgba(0,0,0,0.02)]", children: /* @__PURE__ */ jsxs6("tr", { children: [
2739
+ /* @__PURE__ */ jsx6("td", { className: "border-b border-l border-slate-200 p-1.5 text-center font-bold text-slate-600 text-xs", children: '\u05E1\u05D4"\u05DB' }),
2740
+ filteredColumns.map((col) => {
2741
+ var _a;
2742
+ const typeUpper = (_a = col.type) == null ? void 0 : _a.toUpperCase();
2743
+ const isNumeric = typeUpper === "CURRENCY" || typeUpper === "NUMBER";
2744
+ const total = columnTotals[col.name];
2745
+ let displayVal = "";
2746
+ if (isNumeric && total !== void 0) {
2747
+ displayVal = new Intl.NumberFormat("en-US").format(total);
2748
+ if (typeUpper === "CURRENCY") {
2749
+ const symbol = getCurrencySymbol(col.currencySymbol);
2750
+ displayVal = symbol ? `${symbol} ${displayVal}` : displayVal;
2751
+ }
2752
+ }
2753
+ return /* @__PURE__ */ jsx6("td", { className: "border-b border-l border-slate-200 p-2 text-sm font-semibold text-slate-700 bg-slate-50 relative", children: isNumeric && /* @__PURE__ */ jsx6("div", { className: "flex items-center text-right justify-start", children: /* @__PURE__ */ jsx6("span", { className: "truncate", children: displayVal }) }) }, col.name);
2754
+ }),
2755
+ /* @__PURE__ */ jsx6("td", { className: "border-b border-slate-200 bg-slate-50 pointer-events-none" })
2756
+ ] }) })
2757
+ ] })
2758
+ ] }),
2759
+ /* @__PURE__ */ jsxs6("div", { className: "flex flex-col sm:flex-row items-center justify-between gap-3 px-4 py-3 bg-slate-50 border-t border-slate-200 text-xs text-slate-600 rounded-b-xl", children: [
2760
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
2761
+ /* @__PURE__ */ jsx6("span", { className: "font-medium text-slate-700", children: "\u05E9\u05D5\u05E8\u05D5\u05EA \u05DC\u05E2\u05DE\u05D5\u05D3:" }),
2762
+ /* @__PURE__ */ jsxs6(
2763
+ "select",
2764
+ {
2765
+ value: limit,
2766
+ onChange: (e) => handleLimitChange(Number(e.target.value)),
2767
+ disabled: isLoading || isUpdatingActive,
2768
+ className: "bg-white border border-slate-300 rounded-lg px-2.5 py-1 text-slate-700 font-semibold shadow-2xs cursor-pointer focus:outline-none focus:ring-2 focus:ring-brand disabled:opacity-50",
2769
+ children: [
2770
+ /* @__PURE__ */ jsx6("option", { value: 20, children: "20" }),
2771
+ /* @__PURE__ */ jsx6("option", { value: 50, children: "50" }),
2772
+ /* @__PURE__ */ jsx6("option", { value: 100, children: "100" })
2773
+ ]
2774
+ }
2775
+ )
2776
+ ] }),
2777
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-4", children: [
2778
+ /* @__PURE__ */ jsxs6("span", { className: "font-medium text-slate-600", children: [
2779
+ "\u05E2\u05DE\u05D5\u05D3 ",
2780
+ /* @__PURE__ */ jsx6("strong", { className: "text-slate-900", children: page }),
2781
+ " \u05DE\u05EA\u05D5\u05DA ",
2782
+ /* @__PURE__ */ jsx6("strong", { className: "text-slate-900", children: totalPages || 1 }),
2783
+ totalRecords > 0 && /* @__PURE__ */ jsxs6("span", { className: "text-slate-500 font-normal", children: [
2784
+ " (",
2785
+ totalRecords,
2786
+ ' \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D1\u05E1\u05D4"\u05DB)'
2787
+ ] })
2788
+ ] }),
2789
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5", children: [
2790
+ /* @__PURE__ */ jsx6(
2791
+ "button",
2792
+ {
2793
+ onClick: handlePrevPage,
2794
+ disabled: page <= 1 || isLoading || isUpdatingActive,
2795
+ className: "px-3 py-1.5 bg-white border border-slate-300 hover:bg-slate-100 font-semibold text-slate-700 rounded-lg shadow-2xs disabled:opacity-40 disabled:pointer-events-none transition-colors cursor-pointer",
2796
+ title: "\u05E2\u05DE\u05D5\u05D3 \u05E7\u05D5\u05D3\u05DD",
2797
+ children: "\u05D4\u05E7\u05D5\u05D3\u05DD"
2798
+ }
2799
+ ),
2800
+ /* @__PURE__ */ jsx6(
2801
+ "button",
2802
+ {
2803
+ onClick: handleNextPage,
2804
+ disabled: page >= totalPages || isLoading || isUpdatingActive,
2805
+ className: "px-3 py-1.5 bg-white border border-slate-300 hover:bg-slate-100 font-semibold text-slate-700 rounded-lg shadow-2xs disabled:opacity-40 disabled:pointer-events-none transition-colors cursor-pointer",
2806
+ title: "\u05E2\u05DE\u05D5\u05D3 \u05D4\u05D1\u05D0",
2807
+ children: "\u05D4\u05D1\u05D0"
2808
+ }
2809
+ )
2810
+ ] })
1444
2811
  ] })
1445
2812
  ] })
1446
2813
  ]
1447
2814
  }
1448
2815
  );
1449
2816
  }
2817
+ function ExpandedSubTables({
2818
+ rowId,
2819
+ rowData,
2820
+ subTableColumns,
2821
+ filteredColumnsLength,
2822
+ handleCellChange,
2823
+ context
2824
+ }) {
2825
+ var _a;
2826
+ const [activeTab, setActiveTab] = useState5((_a = subTableColumns[0]) == null ? void 0 : _a.name);
2827
+ if (subTableColumns.length === 0) return null;
2828
+ if (subTableColumns.length === 1) {
2829
+ const stCol = subTableColumns[0];
2830
+ return /* @__PURE__ */ jsx6("tr", { children: /* @__PURE__ */ jsx6("td", { colSpan: filteredColumnsLength + 2, className: "p-0 border-b border-slate-200 bg-slate-50/50", children: /* @__PURE__ */ jsx6(
2831
+ SubTableViewer,
2832
+ {
2833
+ rowId,
2834
+ column: stCol,
2835
+ value: (rowData == null ? void 0 : rowData[stCol.name]) || [],
2836
+ onChange: (newVal) => handleCellChange(rowId, stCol.name, newVal),
2837
+ context
2838
+ }
2839
+ ) }) });
2840
+ }
2841
+ const activeCol = subTableColumns.find((c) => c.name === activeTab) || subTableColumns[0];
2842
+ return /* @__PURE__ */ jsx6("tr", { children: /* @__PURE__ */ jsxs6("td", { colSpan: filteredColumnsLength + 2, className: "p-0 border-b border-slate-200 bg-slate-50/50 overflow-hidden", children: [
2843
+ /* @__PURE__ */ jsx6("div", { className: "flex border-b border-slate-200 bg-slate-100/50 px-2 pt-2 gap-1 overflow-hidden", children: subTableColumns.map((col) => /* @__PURE__ */ jsx6(
2844
+ "button",
2845
+ {
2846
+ type: "button",
2847
+ onClick: () => setActiveTab(col.name),
2848
+ className: `px-4 py-2 text-sm font-semibold rounded-t-lg transition-colors whitespace-nowrap cursor-pointer relative top-[1px] ${activeTab === col.name ? "bg-white border border-slate-200 border-b-transparent text-brand" : "bg-transparent border border-transparent text-slate-500 hover:text-slate-700 hover:bg-slate-200/50"}`,
2849
+ children: col.name
2850
+ },
2851
+ col.name
2852
+ )) }),
2853
+ /* @__PURE__ */ jsx6("div", { className: "bg-white", children: /* @__PURE__ */ jsx6(
2854
+ SubTableViewer,
2855
+ {
2856
+ rowId,
2857
+ column: activeCol,
2858
+ value: (rowData == null ? void 0 : rowData[activeCol.name]) || [],
2859
+ onChange: (newVal) => handleCellChange(rowId, activeCol.name, newVal),
2860
+ context
2861
+ },
2862
+ activeCol.name
2863
+ ) })
2864
+ ] }) });
2865
+ }
1450
2866
 
1451
2867
  // src/components/TableBlock/TableBlock.tsx
1452
- import { jsx as jsx6 } from "react/jsx-runtime";
2868
+ import { jsx as jsx7 } from "react/jsx-runtime";
1453
2869
  function TableBlock(props) {
1454
- return /* @__PURE__ */ jsx6(TableBlockClient, __spreadValues({}, props));
2870
+ return /* @__PURE__ */ jsx7(TableBlockClient, __spreadValues({}, props));
1455
2871
  }
1456
2872
 
1457
2873
  // src/registry.ts