@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/globals.css +334 -2
- package/dist/index.d.mts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +1597 -181
- package/dist/index.mjs +1598 -182
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -77,7 +77,7 @@ function cn(...inputs) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// src/components/TableBlock/TableBlockClient.tsx
|
|
80
|
-
var
|
|
80
|
+
var import_react7 = __toESM(require("react"));
|
|
81
81
|
|
|
82
82
|
// src/hooks/useToast.ts
|
|
83
83
|
var import_react2 = require("react");
|
|
@@ -280,12 +280,36 @@ function useToast() {
|
|
|
280
280
|
var import_react5 = __toESM(require("react"));
|
|
281
281
|
|
|
282
282
|
// src/components/TableBlock/fields/DrivePickerCell.tsx
|
|
283
|
-
var import_react3 = require("react");
|
|
283
|
+
var import_react3 = __toESM(require("react"));
|
|
284
284
|
var import_lucide_react2 = require("lucide-react");
|
|
285
285
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
286
|
-
function DrivePickerCell({ value, onChange, tableName, onMarkForDeletion, context }) {
|
|
286
|
+
function DrivePickerCell({ value, onChange, tableName, column, rowId, onMarkForDeletion, context }) {
|
|
287
287
|
const [isOpening, setIsOpening] = (0, import_react3.useState)(false);
|
|
288
288
|
const { showError } = useToast();
|
|
289
|
+
const cellContextId = import_react3.default.useMemo(() => {
|
|
290
|
+
const colName = (column == null ? void 0 : column.name) || "col";
|
|
291
|
+
const rId = rowId || "row";
|
|
292
|
+
return `${rId}-${colName}`;
|
|
293
|
+
}, [rowId, column == null ? void 0 : column.name]);
|
|
294
|
+
const fileList = import_react3.default.useMemo(() => {
|
|
295
|
+
if (!value) return [];
|
|
296
|
+
if (Array.isArray(value)) return value;
|
|
297
|
+
if (typeof value === "object" && (value.fileId || value.fileUrl || value.url)) {
|
|
298
|
+
return [value];
|
|
299
|
+
}
|
|
300
|
+
return [];
|
|
301
|
+
}, [value]);
|
|
302
|
+
(0, import_react3.useEffect)(() => {
|
|
303
|
+
const handleMessage = (event) => {
|
|
304
|
+
var _a, _b, _c;
|
|
305
|
+
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) {
|
|
306
|
+
const incoming = Array.isArray(event.data.payload) ? event.data.payload : [event.data.payload];
|
|
307
|
+
onChange([...fileList, ...incoming]);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
window.addEventListener("message", handleMessage);
|
|
311
|
+
return () => window.removeEventListener("message", handleMessage);
|
|
312
|
+
}, [fileList, onChange, cellContextId]);
|
|
289
313
|
const openUploadPopup = async () => {
|
|
290
314
|
if (isOpening) return;
|
|
291
315
|
setIsOpening(true);
|
|
@@ -314,34 +338,35 @@ function DrivePickerCell({ value, onChange, tableName, onMarkForDeletion, contex
|
|
|
314
338
|
} catch (e) {
|
|
315
339
|
}
|
|
316
340
|
}
|
|
317
|
-
const actionResult = await context.executeAction("UPLOAD_DRIVE_FILE", { tableName });
|
|
341
|
+
const actionResult = await context.executeAction("UPLOAD_DRIVE_FILE", { tableName, cellContextId });
|
|
318
342
|
const folderId = actionResult == null ? void 0 : actionResult.folderId;
|
|
319
|
-
|
|
343
|
+
let pickerUrl = (actionResult == null ? void 0 : actionResult.pickerUrl) || (folderId ? `/embed/picker?folderId=${encodeURIComponent(folderId)}` : `/embed/picker`);
|
|
344
|
+
if (!pickerUrl.includes("cellContextId=")) {
|
|
345
|
+
const separator = pickerUrl.includes("?") ? "&" : "?";
|
|
346
|
+
pickerUrl = `${pickerUrl}${separator}cellContextId=${encodeURIComponent(cellContextId)}`;
|
|
347
|
+
}
|
|
320
348
|
if (popup) {
|
|
321
349
|
popup.location.href = pickerUrl;
|
|
322
350
|
}
|
|
323
|
-
|
|
351
|
+
await new Promise((resolve) => {
|
|
324
352
|
let timer = null;
|
|
325
|
-
const
|
|
353
|
+
const handlePopupMessage = (event) => {
|
|
326
354
|
var _a, _b;
|
|
327
|
-
if (((_a = event.data) == null ? void 0 : _a.type) === "DRIVE_UPLOAD_SUCCESS" && ((_b = event.data) == null ? void 0 : _b.
|
|
328
|
-
window.removeEventListener("message",
|
|
355
|
+
if (((_a = event.data) == null ? void 0 : _a.type) === "DRIVE_UPLOAD_SUCCESS" && ((_b = event.data) == null ? void 0 : _b.cellContextId) === cellContextId) {
|
|
356
|
+
window.removeEventListener("message", handlePopupMessage);
|
|
329
357
|
if (timer) clearInterval(timer);
|
|
330
|
-
resolve(
|
|
358
|
+
resolve();
|
|
331
359
|
}
|
|
332
360
|
};
|
|
333
361
|
timer = setInterval(() => {
|
|
334
362
|
if (!popup || popup.closed) {
|
|
335
363
|
if (timer) clearInterval(timer);
|
|
336
|
-
window.removeEventListener("message",
|
|
337
|
-
resolve(
|
|
364
|
+
window.removeEventListener("message", handlePopupMessage);
|
|
365
|
+
resolve();
|
|
338
366
|
}
|
|
339
367
|
}, 800);
|
|
340
|
-
window.addEventListener("message",
|
|
368
|
+
window.addEventListener("message", handlePopupMessage);
|
|
341
369
|
});
|
|
342
|
-
if (newFile) {
|
|
343
|
-
onChange(newFile);
|
|
344
|
-
}
|
|
345
370
|
}
|
|
346
371
|
} catch (error) {
|
|
347
372
|
console.error("[DrivePickerCell] Error uploading file:", error);
|
|
@@ -351,40 +376,15 @@ function DrivePickerCell({ value, onChange, tableName, onMarkForDeletion, contex
|
|
|
351
376
|
setIsOpening(false);
|
|
352
377
|
}
|
|
353
378
|
};
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
title: value.fileName || "Open file",
|
|
364
|
-
children: [
|
|
365
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.FileText, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
366
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate", children: value.fileName || "Unnamed file" })
|
|
367
|
-
]
|
|
368
|
-
}
|
|
369
|
-
),
|
|
370
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
371
|
-
"button",
|
|
372
|
-
{
|
|
373
|
-
type: "button",
|
|
374
|
-
onClick: () => {
|
|
375
|
-
if ((value == null ? void 0 : value.fileId) && onMarkForDeletion) {
|
|
376
|
-
onMarkForDeletion(value.fileId);
|
|
377
|
-
}
|
|
378
|
-
onChange(null);
|
|
379
|
-
},
|
|
380
|
-
className: "shrink-0 p-0.5 rounded hover:bg-rose-100 text-slate-400 hover:text-rose-500 transition-colors",
|
|
381
|
-
title: "Remove file",
|
|
382
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.X, { className: "w-3.5 h-3.5" })
|
|
383
|
-
}
|
|
384
|
-
)
|
|
385
|
-
] });
|
|
386
|
-
}
|
|
387
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center w-full h-8 px-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
379
|
+
const handleRemoveFile = (indexToRemove) => {
|
|
380
|
+
const fileToRemove = fileList[indexToRemove];
|
|
381
|
+
if ((fileToRemove == null ? void 0 : fileToRemove.fileId) && onMarkForDeletion) {
|
|
382
|
+
onMarkForDeletion(fileToRemove.fileId);
|
|
383
|
+
}
|
|
384
|
+
const updated = fileList.filter((_, idx) => idx !== indexToRemove);
|
|
385
|
+
onChange(updated);
|
|
386
|
+
};
|
|
387
|
+
const renderUploadButton = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center w-full h-8 px-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
388
388
|
"button",
|
|
389
389
|
{
|
|
390
390
|
type: "button",
|
|
@@ -397,6 +397,38 @@ function DrivePickerCell({ value, onChange, tableName, onMarkForDeletion, contex
|
|
|
397
397
|
]
|
|
398
398
|
}
|
|
399
399
|
) });
|
|
400
|
+
if (fileList.length > 0) {
|
|
401
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col w-full py-1 gap-1", children: [
|
|
402
|
+
fileList.map((file, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center w-full h-8 px-2 gap-1", children: [
|
|
403
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
404
|
+
"a",
|
|
405
|
+
{
|
|
406
|
+
href: file.fileUrl || file.url,
|
|
407
|
+
target: "_blank",
|
|
408
|
+
rel: "noopener noreferrer",
|
|
409
|
+
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",
|
|
410
|
+
title: file.fileName || "Open file",
|
|
411
|
+
children: [
|
|
412
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.FileText, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
413
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate", children: file.fileName || "Unnamed file" })
|
|
414
|
+
]
|
|
415
|
+
}
|
|
416
|
+
),
|
|
417
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
418
|
+
"button",
|
|
419
|
+
{
|
|
420
|
+
type: "button",
|
|
421
|
+
onClick: () => handleRemoveFile(idx),
|
|
422
|
+
className: "shrink-0 p-0.5 rounded hover:bg-rose-100 text-slate-400 hover:text-rose-500 transition-colors",
|
|
423
|
+
title: "Remove file",
|
|
424
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.X, { className: "w-3.5 h-3.5" })
|
|
425
|
+
}
|
|
426
|
+
)
|
|
427
|
+
] }, file.fileId || idx)),
|
|
428
|
+
renderUploadButton()
|
|
429
|
+
] });
|
|
430
|
+
}
|
|
431
|
+
return renderUploadButton();
|
|
400
432
|
}
|
|
401
433
|
|
|
402
434
|
// src/components/TableBlock/fields/DatePickerCell.tsx
|
|
@@ -742,15 +774,19 @@ var FORM_INPUTS = {
|
|
|
742
774
|
DATE: ({ value, onChange, column }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DatePickerCell, { value, onChange, column }),
|
|
743
775
|
CURRENCY: ({ value, column, onChange }) => {
|
|
744
776
|
const symbol = getCurrencySymbol(column.currencySymbol);
|
|
777
|
+
const [isFocused, setIsFocused] = import_react5.default.useState(false);
|
|
778
|
+
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 : "";
|
|
745
779
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative w-full h-full", children: [
|
|
746
780
|
symbol && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "absolute left-2 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none text-sm", children: symbol }),
|
|
747
781
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
748
782
|
"input",
|
|
749
783
|
{
|
|
750
|
-
type: "number",
|
|
784
|
+
type: isFocused ? "number" : "text",
|
|
751
785
|
step: "any",
|
|
752
786
|
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`,
|
|
753
|
-
value:
|
|
787
|
+
value: displayValue,
|
|
788
|
+
onFocus: () => setIsFocused(true),
|
|
789
|
+
onBlur: () => setIsFocused(false),
|
|
754
790
|
onChange: (e) => {
|
|
755
791
|
const val = e.target.value;
|
|
756
792
|
onChange(val === "" ? "" : Number(val));
|
|
@@ -827,11 +863,13 @@ var FORM_INPUTS = {
|
|
|
827
863
|
className: "w-4 h-4 rounded border-slate-300 text-brand"
|
|
828
864
|
}
|
|
829
865
|
) }),
|
|
830
|
-
GOOGLE_DRIVE_FILE: ({ value, onChange, context }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
866
|
+
GOOGLE_DRIVE_FILE: ({ value, onChange, column, rowId, context }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
831
867
|
DrivePickerCell,
|
|
832
868
|
{
|
|
833
|
-
value: value ||
|
|
869
|
+
value: value || [],
|
|
834
870
|
onChange,
|
|
871
|
+
column,
|
|
872
|
+
rowId,
|
|
835
873
|
tableName: context == null ? void 0 : context.tableName,
|
|
836
874
|
onMarkForDeletion: context == null ? void 0 : context.onMarkForDeletion,
|
|
837
875
|
context
|
|
@@ -889,6 +927,228 @@ var FORM_INPUTS = {
|
|
|
889
927
|
}
|
|
890
928
|
}
|
|
891
929
|
),
|
|
930
|
+
PHONE: ({ value, onChange }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
931
|
+
"input",
|
|
932
|
+
{
|
|
933
|
+
type: "tel",
|
|
934
|
+
className: "w-full h-8 px-2 bg-transparent focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 text-sm",
|
|
935
|
+
value: value !== void 0 && value !== null ? String(value) : "",
|
|
936
|
+
onChange: (e) => onChange(e.target.value),
|
|
937
|
+
dir: "ltr"
|
|
938
|
+
}
|
|
939
|
+
),
|
|
940
|
+
GOOGLE_CONTACT: ({ value, column, rowId, onChange, context }) => {
|
|
941
|
+
const [isSyncing, setIsSyncing] = import_react5.default.useState(false);
|
|
942
|
+
const { addToast } = useToast();
|
|
943
|
+
const googleClientIdRef = import_react5.default.useRef(null);
|
|
944
|
+
const ensureGisScript = import_react5.default.useCallback(() => {
|
|
945
|
+
return new Promise((resolve, reject) => {
|
|
946
|
+
var _a, _b, _c;
|
|
947
|
+
if ((_c = (_b = (_a = window.google) == null ? void 0 : _a.accounts) == null ? void 0 : _b.oauth2) == null ? void 0 : _c.initTokenClient) {
|
|
948
|
+
resolve();
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if (document.querySelector('script[src*="accounts.google.com/gsi/client"]')) {
|
|
952
|
+
const waitForLoad = setInterval(() => {
|
|
953
|
+
var _a2, _b2, _c2;
|
|
954
|
+
if ((_c2 = (_b2 = (_a2 = window.google) == null ? void 0 : _a2.accounts) == null ? void 0 : _b2.oauth2) == null ? void 0 : _c2.initTokenClient) {
|
|
955
|
+
clearInterval(waitForLoad);
|
|
956
|
+
resolve();
|
|
957
|
+
}
|
|
958
|
+
}, 100);
|
|
959
|
+
setTimeout(() => {
|
|
960
|
+
clearInterval(waitForLoad);
|
|
961
|
+
reject(new Error("GIS script load timeout"));
|
|
962
|
+
}, 1e4);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
const script = document.createElement("script");
|
|
966
|
+
script.src = "https://accounts.google.com/gsi/client";
|
|
967
|
+
script.async = true;
|
|
968
|
+
script.onload = () => resolve();
|
|
969
|
+
script.onerror = () => reject(new Error("Failed to load Google Identity Services"));
|
|
970
|
+
document.head.appendChild(script);
|
|
971
|
+
});
|
|
972
|
+
}, []);
|
|
973
|
+
const requestGisToken = import_react5.default.useCallback(async (hint) => {
|
|
974
|
+
await ensureGisScript();
|
|
975
|
+
if (!googleClientIdRef.current && (context == null ? void 0 : context.executeAction)) {
|
|
976
|
+
try {
|
|
977
|
+
const config = await context.executeAction("GET_GOOGLE_CONFIG", {});
|
|
978
|
+
googleClientIdRef.current = (config == null ? void 0 : config.clientId) || "";
|
|
979
|
+
} catch (e) {
|
|
980
|
+
throw new Error("Failed to fetch Google configuration");
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
const clientId = googleClientIdRef.current;
|
|
984
|
+
if (!clientId) {
|
|
985
|
+
throw new Error("Google Client ID is not configured");
|
|
986
|
+
}
|
|
987
|
+
const accessToken = await new Promise((resolve, reject) => {
|
|
988
|
+
const tokenClient = window.google.accounts.oauth2.initTokenClient({
|
|
989
|
+
client_id: clientId,
|
|
990
|
+
scope: "https://www.googleapis.com/auth/contacts https://www.googleapis.com/auth/userinfo.email",
|
|
991
|
+
prompt: hint ? "" : "select_account",
|
|
992
|
+
hint: hint || void 0,
|
|
993
|
+
callback: (tokenResponse) => {
|
|
994
|
+
if (tokenResponse == null ? void 0 : tokenResponse.access_token) {
|
|
995
|
+
resolve(tokenResponse.access_token);
|
|
996
|
+
} else {
|
|
997
|
+
reject(new Error("Google authorization was cancelled or failed"));
|
|
998
|
+
}
|
|
999
|
+
},
|
|
1000
|
+
error_callback: (err) => {
|
|
1001
|
+
reject(new Error((err == null ? void 0 : err.message) || "Google authorization error"));
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
tokenClient.requestAccessToken({ hint: hint || void 0 });
|
|
1005
|
+
});
|
|
1006
|
+
let email = "";
|
|
1007
|
+
try {
|
|
1008
|
+
const res = await fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
|
|
1009
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
1010
|
+
});
|
|
1011
|
+
if (res.ok) {
|
|
1012
|
+
const info = await res.json();
|
|
1013
|
+
email = info.email || "";
|
|
1014
|
+
}
|
|
1015
|
+
} catch (e) {
|
|
1016
|
+
}
|
|
1017
|
+
return { accessToken, email };
|
|
1018
|
+
}, [ensureGisScript, context]);
|
|
1019
|
+
const handleSync = async () => {
|
|
1020
|
+
var _a, _b, _c;
|
|
1021
|
+
if (!(context == null ? void 0 : context.executeAction)) return;
|
|
1022
|
+
setIsSyncing(true);
|
|
1023
|
+
try {
|
|
1024
|
+
const mapping = column.contactMapping || {};
|
|
1025
|
+
const rowData = context.rowData || {};
|
|
1026
|
+
let hasPhone = false;
|
|
1027
|
+
if (mapping.phoneCol && rowData[mapping.phoneCol]) hasPhone = true;
|
|
1028
|
+
let hasName = false;
|
|
1029
|
+
if (mapping.nameParts && Array.isArray(mapping.nameParts)) {
|
|
1030
|
+
for (const part of mapping.nameParts) {
|
|
1031
|
+
if (part.type === "LOCAL" && rowData[part.columnId]) hasName = true;
|
|
1032
|
+
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;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
if (!hasPhone) {
|
|
1036
|
+
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" });
|
|
1037
|
+
setIsSyncing(false);
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
if (!hasName) {
|
|
1041
|
+
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" });
|
|
1042
|
+
setIsSyncing(false);
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
let accessToken;
|
|
1046
|
+
let userEmail = "";
|
|
1047
|
+
try {
|
|
1048
|
+
const gisResult = await requestGisToken();
|
|
1049
|
+
accessToken = gisResult.accessToken;
|
|
1050
|
+
userEmail = gisResult.email;
|
|
1051
|
+
} catch (gisError) {
|
|
1052
|
+
addToast({ type: "warning", message: gisError.message || "\u05D4\u05D4\u05E8\u05E9\u05D0\u05D4 \u05DC\u05D2\u05D5\u05D2\u05DC \u05E0\u05DB\u05E9\u05DC\u05D4" });
|
|
1053
|
+
setIsSyncing(false);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
const payload = {
|
|
1057
|
+
nameParts: mapping.nameParts || [],
|
|
1058
|
+
phoneCol: mapping.phoneCol,
|
|
1059
|
+
emailCol: mapping.emailCol,
|
|
1060
|
+
localRowData: rowData,
|
|
1061
|
+
parentRecord: context.parentRecord,
|
|
1062
|
+
accessToken,
|
|
1063
|
+
userEmail
|
|
1064
|
+
};
|
|
1065
|
+
const res = await context.executeAction("SYNC_GOOGLE_CONTACT", payload);
|
|
1066
|
+
if (res && res.synced) {
|
|
1067
|
+
if (context.updateQuietly) {
|
|
1068
|
+
context.updateQuietly(rowId, column.name, res);
|
|
1069
|
+
} else {
|
|
1070
|
+
onChange(res);
|
|
1071
|
+
}
|
|
1072
|
+
if (context.tableId && !rowId.startsWith("temp_")) {
|
|
1073
|
+
await context.executeAction("UPDATE_RECORD", {
|
|
1074
|
+
tableId: context.tableId,
|
|
1075
|
+
recordId: rowId,
|
|
1076
|
+
data: { [column.name]: res }
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
console.error("Failed to sync contact:", error);
|
|
1082
|
+
addToast({ type: "warning", message: "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E1\u05E0\u05DB\u05E8\u05D5\u05DF \u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8: " + error.message });
|
|
1083
|
+
} finally {
|
|
1084
|
+
setIsSyncing(false);
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
const handleUnlink = async () => {
|
|
1088
|
+
let accessToken;
|
|
1089
|
+
try {
|
|
1090
|
+
let emailHint = value == null ? void 0 : value.userEmail;
|
|
1091
|
+
if (!emailHint && (value == null ? void 0 : value.url)) {
|
|
1092
|
+
try {
|
|
1093
|
+
const parsed = new URL(value.url);
|
|
1094
|
+
emailHint = parsed.searchParams.get("authuser") || void 0;
|
|
1095
|
+
} catch (e) {
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
const auth = await requestGisToken(emailHint);
|
|
1099
|
+
accessToken = auth.accessToken;
|
|
1100
|
+
} catch (err) {
|
|
1101
|
+
console.warn("[handleUnlink] Could not obtain token via GIS, falling back to server default token:", err);
|
|
1102
|
+
}
|
|
1103
|
+
if ((value == null ? void 0 : value.resourceName) && (context == null ? void 0 : context.executeAction)) {
|
|
1104
|
+
await context.executeAction("DELETE_GOOGLE_CONTACT", {
|
|
1105
|
+
resourceName: value.resourceName,
|
|
1106
|
+
accessToken
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
if (context == null ? void 0 : context.updateQuietly) {
|
|
1110
|
+
context.updateQuietly(rowId, column.name, null);
|
|
1111
|
+
} else {
|
|
1112
|
+
onChange(null);
|
|
1113
|
+
}
|
|
1114
|
+
if ((context == null ? void 0 : context.tableId) && (context == null ? void 0 : context.executeAction) && !rowId.startsWith("temp_")) {
|
|
1115
|
+
await context.executeAction("UPDATE_RECORD", {
|
|
1116
|
+
tableId: context.tableId,
|
|
1117
|
+
recordId: rowId,
|
|
1118
|
+
data: { [column.name]: null }
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
if (value && value.synced && value.url) {
|
|
1123
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 h-8 px-2 group", children: [
|
|
1124
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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" }),
|
|
1125
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1126
|
+
"button",
|
|
1127
|
+
{
|
|
1128
|
+
onClick: handleUnlink,
|
|
1129
|
+
className: "text-slate-400 hover:text-rose-500 opacity-0 group-hover:opacity-100 transition-opacity p-1",
|
|
1130
|
+
title: "\u05D1\u05D8\u05DC \u05E1\u05E0\u05DB\u05E8\u05D5\u05DF",
|
|
1131
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M6 18L18 6M6 6l12 12" }) })
|
|
1132
|
+
}
|
|
1133
|
+
)
|
|
1134
|
+
] });
|
|
1135
|
+
}
|
|
1136
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex items-center h-8 px-2", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1137
|
+
"button",
|
|
1138
|
+
{
|
|
1139
|
+
onClick: handleSync,
|
|
1140
|
+
disabled: isSyncing,
|
|
1141
|
+
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",
|
|
1142
|
+
children: [
|
|
1143
|
+
isSyncing ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { className: "w-3.5 h-3.5 animate-spin", viewBox: "0 0 24 24", fill: "none", children: [
|
|
1144
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
1145
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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" })
|
|
1146
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M12 4v16m8-8H4" }) }),
|
|
1147
|
+
"\u05E1\u05E0\u05DB\u05E8\u05DF \u05DC\u05D2\u05D5\u05D2\u05DC"
|
|
1148
|
+
]
|
|
1149
|
+
}
|
|
1150
|
+
) });
|
|
1151
|
+
},
|
|
892
1152
|
DEFAULT: ({ value, onChange }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
893
1153
|
"input",
|
|
894
1154
|
{
|
|
@@ -897,11 +1157,459 @@ var FORM_INPUTS = {
|
|
|
897
1157
|
value: value !== void 0 && value !== null ? String(value) : "",
|
|
898
1158
|
onChange: (e) => onChange(e.target.value)
|
|
899
1159
|
}
|
|
900
|
-
)
|
|
1160
|
+
),
|
|
1161
|
+
SUB_TABLE: ({ value, column }) => {
|
|
1162
|
+
if (!Array.isArray(value)) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "px-2 py-1 flex items-center h-full text-slate-300 text-xs italic", children: "\u05E8\u05D9\u05E7" });
|
|
1163
|
+
const selected = value.filter((e) => e == null ? void 0 : e.isSelected);
|
|
1164
|
+
if (selected.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "px-2 py-1 flex items-center h-full text-slate-300 text-xs italic", children: "\u05E8\u05D9\u05E7" });
|
|
1165
|
+
const displayFields = column.subTableDisplayFields || [];
|
|
1166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "px-1.5 py-1 flex flex-wrap items-center gap-1 h-full", children: selected.map((entry, idx) => {
|
|
1167
|
+
var _a;
|
|
1168
|
+
let label = "";
|
|
1169
|
+
if (entry._resolvedData && displayFields.length > 0) {
|
|
1170
|
+
label = displayFields.map((f) => String(entry._resolvedData[f] || "")).filter(Boolean).join(" ");
|
|
1171
|
+
}
|
|
1172
|
+
if (!label) label = ((_a = entry.recordId) == null ? void 0 : _a.slice(-6)) || "...";
|
|
1173
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1174
|
+
"span",
|
|
1175
|
+
{
|
|
1176
|
+
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",
|
|
1177
|
+
title: label,
|
|
1178
|
+
children: label
|
|
1179
|
+
},
|
|
1180
|
+
entry.recordId || idx
|
|
1181
|
+
);
|
|
1182
|
+
}) });
|
|
1183
|
+
}
|
|
901
1184
|
};
|
|
902
1185
|
|
|
903
|
-
// src/components/TableBlock/
|
|
1186
|
+
// src/components/TableBlock/fields/SubTableViewer.tsx
|
|
1187
|
+
var import_react6 = require("react");
|
|
904
1188
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1189
|
+
function SubTableViewer({ rowId, column, value, onChange, context }) {
|
|
1190
|
+
var _a;
|
|
1191
|
+
const { addToast } = useToast();
|
|
1192
|
+
const entries = Array.isArray(value) ? value : [];
|
|
1193
|
+
const [linkedRecords, setLinkedRecords] = (0, import_react6.useState)([]);
|
|
1194
|
+
const [targetSchema, setTargetSchema] = (0, import_react6.useState)(null);
|
|
1195
|
+
const [isLoading, setIsLoading] = (0, import_react6.useState)(true);
|
|
1196
|
+
const [isSearchOpen, setIsSearchOpen] = (0, import_react6.useState)(false);
|
|
1197
|
+
const [searchQuery, setSearchQuery] = (0, import_react6.useState)("");
|
|
1198
|
+
const [searchResults, setSearchResults] = (0, import_react6.useState)([]);
|
|
1199
|
+
const [isSearching, setIsSearching] = (0, import_react6.useState)(false);
|
|
1200
|
+
const [isCreating, setIsCreating] = (0, import_react6.useState)(false);
|
|
1201
|
+
const [newRecordData, setNewRecordData] = (0, import_react6.useState)({});
|
|
1202
|
+
const searchRef = (0, import_react6.useRef)(null);
|
|
1203
|
+
const searchTimerRef = (0, import_react6.useRef)(null);
|
|
1204
|
+
const executeAction = context == null ? void 0 : context.executeAction;
|
|
1205
|
+
const referencedTableId = column.referencedTableId;
|
|
1206
|
+
const fetchedIdsRef = (0, import_react6.useRef)(/* @__PURE__ */ new Set());
|
|
1207
|
+
(0, import_react6.useEffect)(() => {
|
|
1208
|
+
if (!referencedTableId || !executeAction) {
|
|
1209
|
+
setIsLoading(false);
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const recordIdsToFetch = entries.map((e) => e.recordId).filter((id) => !fetchedIdsRef.current.has(id));
|
|
1213
|
+
if (recordIdsToFetch.length === 0 && targetSchema) return;
|
|
1214
|
+
if (recordIdsToFetch.length > 0) {
|
|
1215
|
+
recordIdsToFetch.forEach((id) => fetchedIdsRef.current.add(id));
|
|
1216
|
+
}
|
|
1217
|
+
setIsLoading(true);
|
|
1218
|
+
executeAction("FETCH_SUB_TABLE_RECORDS", { referencedTableId, recordIds: recordIdsToFetch }).then((res) => {
|
|
1219
|
+
const payload = (res == null ? void 0 : res.data) || res;
|
|
1220
|
+
if (payload == null ? void 0 : payload.records) {
|
|
1221
|
+
setLinkedRecords((prev) => {
|
|
1222
|
+
const map = new Map(prev.map((r) => [r.id, r]));
|
|
1223
|
+
payload.records.forEach((r) => map.set(r.id, r));
|
|
1224
|
+
return Array.from(map.values());
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
if (payload == null ? void 0 : payload.schema) {
|
|
1228
|
+
setTargetSchema(payload.schema);
|
|
1229
|
+
}
|
|
1230
|
+
}).catch(() => {
|
|
1231
|
+
recordIdsToFetch.forEach((id) => fetchedIdsRef.current.delete(id));
|
|
1232
|
+
}).finally(() => setIsLoading(false));
|
|
1233
|
+
}, [referencedTableId, executeAction, entries, targetSchema]);
|
|
1234
|
+
(0, import_react6.useEffect)(() => {
|
|
1235
|
+
const handleClick = (e) => {
|
|
1236
|
+
if (searchRef.current && !searchRef.current.contains(e.target)) {
|
|
1237
|
+
setIsSearchOpen(false);
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
if (isSearchOpen) document.addEventListener("mousedown", handleClick);
|
|
1241
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
1242
|
+
}, [isSearchOpen]);
|
|
1243
|
+
const handleSearch = (0, import_react6.useCallback)((query) => {
|
|
1244
|
+
setSearchQuery(query);
|
|
1245
|
+
if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
|
|
1246
|
+
if (!query.trim()) {
|
|
1247
|
+
setSearchResults([]);
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
searchTimerRef.current = setTimeout(async () => {
|
|
1251
|
+
if (!executeAction || !referencedTableId) return;
|
|
1252
|
+
setIsSearching(true);
|
|
1253
|
+
try {
|
|
1254
|
+
const excludeIds = entries.map((e) => e.recordId);
|
|
1255
|
+
const res = await executeAction("SEARCH_SUB_TABLE_RECORDS", {
|
|
1256
|
+
referencedTableId,
|
|
1257
|
+
query: query.trim(),
|
|
1258
|
+
excludeIds,
|
|
1259
|
+
limit: 15
|
|
1260
|
+
});
|
|
1261
|
+
setSearchResults(Array.isArray(res) ? res : []);
|
|
1262
|
+
} catch (e) {
|
|
1263
|
+
setSearchResults([]);
|
|
1264
|
+
} finally {
|
|
1265
|
+
setIsSearching(false);
|
|
1266
|
+
}
|
|
1267
|
+
}, 300);
|
|
1268
|
+
}, [executeAction, referencedTableId, entries]);
|
|
1269
|
+
const targetColumns = ((targetSchema == null ? void 0 : targetSchema.columns) || []).filter(
|
|
1270
|
+
(c) => {
|
|
1271
|
+
if (c.name === "_id" || c.type === "SUB_TABLE") return false;
|
|
1272
|
+
return true;
|
|
1273
|
+
}
|
|
1274
|
+
);
|
|
1275
|
+
const [relationOptions, setRelationOptions] = (0, import_react6.useState)({});
|
|
1276
|
+
(0, import_react6.useEffect)(() => {
|
|
1277
|
+
if (!executeAction || targetColumns.length === 0) return;
|
|
1278
|
+
const relationCols = targetColumns.filter((c) => {
|
|
1279
|
+
var _a2;
|
|
1280
|
+
return ((_a2 = c.type) == null ? void 0 : _a2.toUpperCase()) === "RELATION" && c.referencedTableId;
|
|
1281
|
+
});
|
|
1282
|
+
relationCols.forEach(async (col) => {
|
|
1283
|
+
try {
|
|
1284
|
+
const options = await executeAction("FETCH_RELATION", {
|
|
1285
|
+
referencedTableId: col.referencedTableId,
|
|
1286
|
+
displayField: col.displayField
|
|
1287
|
+
});
|
|
1288
|
+
if (options && Array.isArray(options)) {
|
|
1289
|
+
setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: options }));
|
|
1290
|
+
}
|
|
1291
|
+
} catch (err) {
|
|
1292
|
+
console.warn(`[SubTableViewer] Could not fetch relation options for column ${col.name}:`, err);
|
|
1293
|
+
}
|
|
1294
|
+
});
|
|
1295
|
+
}, [executeAction, targetColumns]);
|
|
1296
|
+
const refetchRelation = (0, import_react6.useCallback)(async (colName) => {
|
|
1297
|
+
var _a2;
|
|
1298
|
+
const col = targetColumns.find((c) => c.name === colName);
|
|
1299
|
+
if (!col || ((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) !== "RELATION" || !col.referencedTableId || !executeAction) return;
|
|
1300
|
+
try {
|
|
1301
|
+
const options = await executeAction("FETCH_RELATION", { referencedTableId: col.referencedTableId, displayField: col.displayField });
|
|
1302
|
+
if (options && Array.isArray(options)) {
|
|
1303
|
+
setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: options }));
|
|
1304
|
+
}
|
|
1305
|
+
} catch (err) {
|
|
1306
|
+
console.warn(`[SubTableViewer] Could not refetch relation options for column ${col.name}:`, err);
|
|
1307
|
+
}
|
|
1308
|
+
}, [targetColumns, executeAction]);
|
|
1309
|
+
const updateParentLinkage = async (updated) => {
|
|
1310
|
+
const isNewRow = rowId.startsWith("temp_");
|
|
1311
|
+
if (context == null ? void 0 : context.updateQuietly) {
|
|
1312
|
+
context.updateQuietly(rowId, column.name, updated);
|
|
1313
|
+
} else {
|
|
1314
|
+
onChange(updated);
|
|
1315
|
+
}
|
|
1316
|
+
if (executeAction && (context == null ? void 0 : context.parentTableId) && !isNewRow) {
|
|
1317
|
+
try {
|
|
1318
|
+
const payloadToSave = updated.map(({ recordId, isSelected }) => ({ recordId, isSelected }));
|
|
1319
|
+
await executeAction("UPDATE_RECORD", {
|
|
1320
|
+
tableId: context.parentTableId,
|
|
1321
|
+
recordId: rowId,
|
|
1322
|
+
data: { [column.name]: payloadToSave }
|
|
1323
|
+
});
|
|
1324
|
+
if (context == null ? void 0 : context.triggerSaveSuccess) {
|
|
1325
|
+
context.triggerSaveSuccess();
|
|
1326
|
+
}
|
|
1327
|
+
} catch (err) {
|
|
1328
|
+
console.error("Failed to auto-save parent linkage", err);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
const handleLinkRecord = (record) => {
|
|
1333
|
+
fetchedIdsRef.current.add(record.id);
|
|
1334
|
+
const newEntry = { recordId: record.id, isSelected: true, _resolvedData: record.data };
|
|
1335
|
+
const updated = [...entries, newEntry];
|
|
1336
|
+
updateParentLinkage(updated);
|
|
1337
|
+
setLinkedRecords((prev) => {
|
|
1338
|
+
const exists = prev.find((r) => r.id === record.id);
|
|
1339
|
+
if (exists) return prev;
|
|
1340
|
+
return [...prev, record];
|
|
1341
|
+
});
|
|
1342
|
+
setSearchQuery("");
|
|
1343
|
+
setSearchResults([]);
|
|
1344
|
+
setIsSearchOpen(false);
|
|
1345
|
+
};
|
|
1346
|
+
const handleUnlinkRecord = (recordId) => {
|
|
1347
|
+
const updated = entries.filter((e) => e.recordId !== recordId);
|
|
1348
|
+
updateParentLinkage(updated);
|
|
1349
|
+
setLinkedRecords((prev) => prev.filter((r) => r.id !== recordId));
|
|
1350
|
+
};
|
|
1351
|
+
const handleToggleSelected = (recordId) => {
|
|
1352
|
+
const record = linkedRecords.find((r) => r.id === recordId);
|
|
1353
|
+
const updated = entries.map(
|
|
1354
|
+
(e) => e.recordId === recordId ? __spreadProps(__spreadValues({}, e), { isSelected: !e.isSelected, _resolvedData: (record == null ? void 0 : record.data) || e._resolvedData }) : e
|
|
1355
|
+
);
|
|
1356
|
+
updateParentLinkage(updated);
|
|
1357
|
+
};
|
|
1358
|
+
const handleRecordCellChange = async (recordId, colName, value2) => {
|
|
1359
|
+
const updatedRecords = linkedRecords.map(
|
|
1360
|
+
(r) => r.id === recordId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value2 }) }) : r
|
|
1361
|
+
);
|
|
1362
|
+
setLinkedRecords(updatedRecords);
|
|
1363
|
+
const targetRecord = updatedRecords.find((r) => r.id === recordId);
|
|
1364
|
+
const updatedEntries = entries.map(
|
|
1365
|
+
(e) => e.recordId === recordId ? __spreadProps(__spreadValues({}, e), { _resolvedData: targetRecord == null ? void 0 : targetRecord.data }) : e
|
|
1366
|
+
);
|
|
1367
|
+
if (context == null ? void 0 : context.updateQuietly) {
|
|
1368
|
+
context.updateQuietly(rowId, column.name, updatedEntries);
|
|
1369
|
+
} else {
|
|
1370
|
+
onChange(updatedEntries);
|
|
1371
|
+
}
|
|
1372
|
+
if (executeAction && referencedTableId && !recordId.startsWith("temp_")) {
|
|
1373
|
+
try {
|
|
1374
|
+
await executeAction("UPDATE_RECORD", {
|
|
1375
|
+
tableId: referencedTableId,
|
|
1376
|
+
recordId,
|
|
1377
|
+
data: { [colName]: value2 }
|
|
1378
|
+
});
|
|
1379
|
+
if (context == null ? void 0 : context.triggerSaveSuccess) {
|
|
1380
|
+
context.triggerSaveSuccess();
|
|
1381
|
+
}
|
|
1382
|
+
} catch (err) {
|
|
1383
|
+
console.error("[SubTableViewer] Failed to auto-save linked record field", err);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
const handleCreateRecord = async () => {
|
|
1388
|
+
var _a2;
|
|
1389
|
+
if (!executeAction || !referencedTableId) return;
|
|
1390
|
+
for (const col of targetColumns) {
|
|
1391
|
+
if (col.isRequired) {
|
|
1392
|
+
const val = newRecordData[col.name];
|
|
1393
|
+
if (val === void 0 || val === null || typeof val === "string" && val.trim() === "") {
|
|
1394
|
+
addToast({
|
|
1395
|
+
type: "error",
|
|
1396
|
+
message: `Validation Error: Field '${col.name}' is required but was not provided.`
|
|
1397
|
+
});
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
try {
|
|
1403
|
+
const res = await executeAction("CREATE_RECORD", {
|
|
1404
|
+
tableId: referencedTableId,
|
|
1405
|
+
records: [newRecordData]
|
|
1406
|
+
});
|
|
1407
|
+
if (res && res.success === false) {
|
|
1408
|
+
throw new Error(res.error || "Action execution failed");
|
|
1409
|
+
}
|
|
1410
|
+
const created = Array.isArray(res) ? res[0] : ((_a2 = res == null ? void 0 : res.data) == null ? void 0 : _a2[0]) || res;
|
|
1411
|
+
if (created) {
|
|
1412
|
+
const newRecord = {
|
|
1413
|
+
id: String(created.id || created._id),
|
|
1414
|
+
data: created.data || newRecordData
|
|
1415
|
+
};
|
|
1416
|
+
handleLinkRecord(newRecord);
|
|
1417
|
+
setIsCreating(false);
|
|
1418
|
+
setNewRecordData({});
|
|
1419
|
+
}
|
|
1420
|
+
} catch (err) {
|
|
1421
|
+
const msg = (err == null ? void 0 : err.message) || String(err);
|
|
1422
|
+
addToast({
|
|
1423
|
+
type: "error",
|
|
1424
|
+
message: msg
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
const getDisplayLabel = (record) => {
|
|
1429
|
+
const fields = column.subTableDisplayFields || [];
|
|
1430
|
+
if (fields.length === 0) {
|
|
1431
|
+
const firstField = Object.keys(record.data).find((k) => k !== "_id");
|
|
1432
|
+
return firstField ? String(record.data[firstField] || "") : record.id;
|
|
1433
|
+
}
|
|
1434
|
+
return fields.map((f) => String(record.data[f] || "")).filter(Boolean).join(" ");
|
|
1435
|
+
};
|
|
1436
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-3 animate-in fade-in slide-in-from-top-2 duration-200", dir: "rtl", children: [
|
|
1437
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-4 mb-2.5", children: [
|
|
1438
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs font-bold text-slate-600", children: (_a = targetSchema == null ? void 0 : targetSchema.name) != null ? _a : column.referencedTableName }) }),
|
|
1439
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
1440
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative", ref: searchRef, children: [
|
|
1441
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1442
|
+
"button",
|
|
1443
|
+
{
|
|
1444
|
+
type: "button",
|
|
1445
|
+
onClick: () => setIsSearchOpen((prev) => !prev),
|
|
1446
|
+
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",
|
|
1447
|
+
children: [
|
|
1448
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }),
|
|
1449
|
+
"\u05E7\u05D9\u05E9\u05D5\u05E8 \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA"
|
|
1450
|
+
]
|
|
1451
|
+
}
|
|
1452
|
+
),
|
|
1453
|
+
isSearchOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("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: [
|
|
1454
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1455
|
+
"input",
|
|
1456
|
+
{
|
|
1457
|
+
type: "text",
|
|
1458
|
+
value: searchQuery,
|
|
1459
|
+
onChange: (e) => handleSearch(e.target.value),
|
|
1460
|
+
placeholder: "\u05D7\u05E4\u05E9 \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA...",
|
|
1461
|
+
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",
|
|
1462
|
+
autoFocus: true
|
|
1463
|
+
}
|
|
1464
|
+
),
|
|
1465
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "max-h-48 overflow-y-auto", children: isSearching ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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__ */ (0, import_jsx_runtime5.jsx)(
|
|
1466
|
+
"button",
|
|
1467
|
+
{
|
|
1468
|
+
type: "button",
|
|
1469
|
+
onClick: () => handleLinkRecord(r),
|
|
1470
|
+
className: "w-full text-right px-2 py-1.5 text-xs hover:bg-blue-50 rounded cursor-pointer transition-colors",
|
|
1471
|
+
children: getDisplayLabel(r)
|
|
1472
|
+
},
|
|
1473
|
+
r.id
|
|
1474
|
+
)) : searchQuery.trim() ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-xs text-slate-400 text-center py-3", children: "\u05D4\u05E7\u05DC\u05D3 \u05DC\u05D7\u05D9\u05E4\u05D5\u05E9..." }) })
|
|
1475
|
+
] })
|
|
1476
|
+
] }),
|
|
1477
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1478
|
+
"button",
|
|
1479
|
+
{
|
|
1480
|
+
type: "button",
|
|
1481
|
+
onClick: () => {
|
|
1482
|
+
setIsCreating(true);
|
|
1483
|
+
const initialData = {};
|
|
1484
|
+
targetColumns.forEach((c) => {
|
|
1485
|
+
if (c.defaultValue !== void 0 && c.defaultValue !== null) {
|
|
1486
|
+
initialData[c.name] = c.type === "NUMBER" || c.type === "CURRENCY" ? parseFloat(c.defaultValue) : c.type === "BOOLEAN" ? c.defaultValue === "true" || c.defaultValue === true : c.defaultValue;
|
|
1487
|
+
} else {
|
|
1488
|
+
if (c.type === "NUMBER" || c.type === "CURRENCY") initialData[c.name] = 0;
|
|
1489
|
+
else if (c.type === "BOOLEAN") initialData[c.name] = false;
|
|
1490
|
+
else initialData[c.name] = "";
|
|
1491
|
+
}
|
|
1492
|
+
});
|
|
1493
|
+
setNewRecordData(initialData);
|
|
1494
|
+
},
|
|
1495
|
+
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",
|
|
1496
|
+
children: [
|
|
1497
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3 h-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M12 4v16m8-8H4" }) }),
|
|
1498
|
+
"\u05E8\u05E9\u05D5\u05DE\u05D4 \u05D7\u05D3\u05E9\u05D4"
|
|
1499
|
+
]
|
|
1500
|
+
}
|
|
1501
|
+
)
|
|
1502
|
+
] })
|
|
1503
|
+
] }),
|
|
1504
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-5 h-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" }) }) : entries.length === 0 && !isCreating ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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__ */ (0, import_jsx_runtime5.jsx)("div", { className: "overflow-x-auto pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("table", { className: "w-full text-xs border-collapse", children: [
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { className: "bg-slate-100/80", children: [
|
|
1506
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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" }),
|
|
1507
|
+
targetColumns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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)),
|
|
1508
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "border border-slate-200 px-2 py-1.5 w-14" })
|
|
1509
|
+
] }) }),
|
|
1510
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tbody", { children: [
|
|
1511
|
+
entries.map((entry) => {
|
|
1512
|
+
const record = linkedRecords.find((r) => r.id === entry.recordId);
|
|
1513
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { className: "hover:bg-blue-50/30 transition-colors", children: [
|
|
1514
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "border border-slate-200 px-2 py-1 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1515
|
+
"input",
|
|
1516
|
+
{
|
|
1517
|
+
type: "checkbox",
|
|
1518
|
+
checked: entry.isSelected,
|
|
1519
|
+
onChange: () => handleToggleSelected(entry.recordId),
|
|
1520
|
+
className: "w-3.5 h-3.5 rounded border-slate-300 text-blue-600 cursor-pointer"
|
|
1521
|
+
}
|
|
1522
|
+
) }),
|
|
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__ */ (0, import_jsx_runtime5.jsx)("td", { className: "border border-slate-200 p-0 relative h-8", children: record ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1528
|
+
InputComponent,
|
|
1529
|
+
{
|
|
1530
|
+
value: (_b = record.data[col.name]) != null ? _b : colType === "BOOLEAN" ? false : "",
|
|
1531
|
+
column: col,
|
|
1532
|
+
rowId: record.id,
|
|
1533
|
+
onChange: (v) => handleRecordCellChange(record.id, 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
|
+
updateRelationOptions: (name, opts) => setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [name]: opts })),
|
|
1538
|
+
refetchRelation,
|
|
1539
|
+
parentRecord: { tableId: (context == null ? void 0 : context.parentTableId) || (context == null ? void 0 : context.tableId), data: context == null ? void 0 : context.rowData },
|
|
1540
|
+
rowData: record.data,
|
|
1541
|
+
tableId: targetSchema == null ? void 0 : targetSchema.id,
|
|
1542
|
+
updateQuietly: (rid, colName, val) => handleRecordCellChange(rid, colName, val)
|
|
1543
|
+
})
|
|
1544
|
+
}
|
|
1545
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-slate-300 italic px-2", children: "\u05D8\u05D5\u05E2\u05DF..." }) }, col.name);
|
|
1546
|
+
}),
|
|
1547
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "border border-slate-200 px-1 py-1 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1548
|
+
"button",
|
|
1549
|
+
{
|
|
1550
|
+
type: "button",
|
|
1551
|
+
onClick: () => handleUnlinkRecord(entry.recordId),
|
|
1552
|
+
className: "text-slate-400 hover:text-rose-500 p-0.5 rounded transition-colors cursor-pointer inline-flex items-center justify-center w-full",
|
|
1553
|
+
title: "\u05D4\u05E1\u05E8 \u05E7\u05D9\u05E9\u05D5\u05E8",
|
|
1554
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M6 18L18 6M6 6l12 12" }) })
|
|
1555
|
+
}
|
|
1556
|
+
) })
|
|
1557
|
+
] }, entry.recordId);
|
|
1558
|
+
}),
|
|
1559
|
+
isCreating && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { className: "bg-emerald-50/40", children: [
|
|
1560
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "border border-emerald-200 px-2 py-1 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-emerald-500 font-bold text-[10px]", children: "\u05D7\u05D3\u05E9" }) }),
|
|
1561
|
+
targetColumns.map((col) => {
|
|
1562
|
+
var _a2, _b;
|
|
1563
|
+
const colType = ((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) || "DEFAULT";
|
|
1564
|
+
const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
|
|
1565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "border border-emerald-200 p-0 relative h-8", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1566
|
+
InputComponent,
|
|
1567
|
+
{
|
|
1568
|
+
value: (_b = newRecordData[col.name]) != null ? _b : colType === "BOOLEAN" ? false : "",
|
|
1569
|
+
column: col,
|
|
1570
|
+
rowId: "new_record",
|
|
1571
|
+
onChange: (v) => setNewRecordData((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: v })),
|
|
1572
|
+
context: __spreadProps(__spreadValues({}, context), {
|
|
1573
|
+
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",
|
|
1574
|
+
relationOptions,
|
|
1575
|
+
refetchRelation
|
|
1576
|
+
})
|
|
1577
|
+
}
|
|
1578
|
+
) }, `new_${col.name}`);
|
|
1579
|
+
}),
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "border border-emerald-200 px-1 py-1 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
1581
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1582
|
+
"button",
|
|
1583
|
+
{
|
|
1584
|
+
type: "button",
|
|
1585
|
+
onClick: handleCreateRecord,
|
|
1586
|
+
className: "p-1 text-emerald-600 hover:bg-emerald-100 rounded transition-colors",
|
|
1587
|
+
title: "\u05E9\u05DE\u05D5\u05E8",
|
|
1588
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M5 13l4 4L19 7" }) })
|
|
1589
|
+
}
|
|
1590
|
+
),
|
|
1591
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1592
|
+
"button",
|
|
1593
|
+
{
|
|
1594
|
+
type: "button",
|
|
1595
|
+
onClick: () => {
|
|
1596
|
+
setIsCreating(false);
|
|
1597
|
+
},
|
|
1598
|
+
className: "p-1 text-rose-500 hover:bg-rose-100 rounded transition-colors",
|
|
1599
|
+
title: "\u05D1\u05D9\u05D8\u05D5\u05DC",
|
|
1600
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M6 18L18 6M6 6l12 12" }) })
|
|
1601
|
+
}
|
|
1602
|
+
)
|
|
1603
|
+
] }) })
|
|
1604
|
+
] })
|
|
1605
|
+
] })
|
|
1606
|
+
] }) })
|
|
1607
|
+
] });
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// src/components/TableBlock/TableBlockClient.tsx
|
|
1611
|
+
var import_lucide_react4 = require("lucide-react");
|
|
1612
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
905
1613
|
function TableBlockClient({
|
|
906
1614
|
data: initialRecords,
|
|
907
1615
|
settings: schema,
|
|
@@ -918,6 +1626,68 @@ function TableBlockClient({
|
|
|
918
1626
|
"data-testid": dataTestId
|
|
919
1627
|
}) {
|
|
920
1628
|
const { showError, addToast } = useToast();
|
|
1629
|
+
const [columnWidths, setColumnWidths] = (0, import_react7.useState)({});
|
|
1630
|
+
const [isMounted, setIsMounted] = (0, import_react7.useState)(false);
|
|
1631
|
+
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";
|
|
1632
|
+
const [page, setPage] = (0, import_react7.useState)(1);
|
|
1633
|
+
const [limit, setLimit] = (0, import_react7.useState)(50);
|
|
1634
|
+
const [totalRecords, setTotalRecords] = (0, import_react7.useState)(0);
|
|
1635
|
+
const [totalPages, setTotalPages] = (0, import_react7.useState)(1);
|
|
1636
|
+
(0, import_react7.useEffect)(() => {
|
|
1637
|
+
if (typeof window !== "undefined") {
|
|
1638
|
+
const savedWidths = localStorage.getItem(`table_widths_${tableKey}`);
|
|
1639
|
+
if (savedWidths) {
|
|
1640
|
+
try {
|
|
1641
|
+
setColumnWidths(JSON.parse(savedWidths));
|
|
1642
|
+
} catch (e) {
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
const savedSummary = localStorage.getItem(`table_summary_${tableKey}`);
|
|
1646
|
+
if (savedSummary !== null) {
|
|
1647
|
+
setShowSummary(savedSummary === "true");
|
|
1648
|
+
}
|
|
1649
|
+
const savedLimit = localStorage.getItem(`table_limit_${tableKey}`);
|
|
1650
|
+
if (savedLimit) {
|
|
1651
|
+
const parsed = parseInt(savedLimit, 10);
|
|
1652
|
+
if ([20, 50, 100].includes(parsed)) {
|
|
1653
|
+
setLimit(parsed);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
setIsMounted(true);
|
|
1658
|
+
}, [tableKey]);
|
|
1659
|
+
const [showSummary, setShowSummary] = (0, import_react7.useState)(false);
|
|
1660
|
+
const toggleSummary = () => {
|
|
1661
|
+
setShowSummary((prev) => {
|
|
1662
|
+
const next = !prev;
|
|
1663
|
+
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";
|
|
1664
|
+
localStorage.setItem(`table_summary_${tableKey2}`, String(next));
|
|
1665
|
+
return next;
|
|
1666
|
+
});
|
|
1667
|
+
};
|
|
1668
|
+
const [resizingCol, setResizingCol] = (0, import_react7.useState)(null);
|
|
1669
|
+
(0, import_react7.useEffect)(() => {
|
|
1670
|
+
if (!resizingCol) return;
|
|
1671
|
+
const handleMouseMove = (e) => {
|
|
1672
|
+
const diffX = resizingCol.startX - e.clientX;
|
|
1673
|
+
const newWidth = Math.max(100, Math.min(500, resizingCol.startWidth + diffX));
|
|
1674
|
+
setColumnWidths((prev) => __spreadProps(__spreadValues({}, prev), { [resizingCol.name]: newWidth }));
|
|
1675
|
+
};
|
|
1676
|
+
const handleMouseUp = () => {
|
|
1677
|
+
setResizingCol(null);
|
|
1678
|
+
setColumnWidths((prev) => {
|
|
1679
|
+
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";
|
|
1680
|
+
localStorage.setItem(`table_widths_${tableKey2}`, JSON.stringify(prev));
|
|
1681
|
+
return prev;
|
|
1682
|
+
});
|
|
1683
|
+
};
|
|
1684
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
1685
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
1686
|
+
return () => {
|
|
1687
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
1688
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
1689
|
+
};
|
|
1690
|
+
}, [resizingCol, schema]);
|
|
921
1691
|
const normalizeRecords = (records) => {
|
|
922
1692
|
return (records || []).map((row) => {
|
|
923
1693
|
var _c;
|
|
@@ -943,18 +1713,38 @@ function TableBlockClient({
|
|
|
943
1713
|
return __spreadProps(__spreadValues({}, row), { id: String(row.id || ""), data: rowData });
|
|
944
1714
|
});
|
|
945
1715
|
};
|
|
946
|
-
const
|
|
947
|
-
|
|
1716
|
+
const mergeColumnDefs = (0, import_react7.useCallback)((existingCols, incomingCols) => {
|
|
1717
|
+
if (!incomingCols || incomingCols.length === 0) return existingCols || [];
|
|
1718
|
+
if (!existingCols || existingCols.length === 0) return incomingCols;
|
|
1719
|
+
const existingMap = new Map(existingCols.map((c) => [c.name, c]));
|
|
1720
|
+
const merged = incomingCols.map((inc) => {
|
|
1721
|
+
const ext = existingMap.get(inc.name);
|
|
1722
|
+
if (ext) {
|
|
1723
|
+
existingMap.delete(inc.name);
|
|
1724
|
+
return __spreadValues(__spreadValues({}, ext), inc);
|
|
1725
|
+
}
|
|
1726
|
+
return inc;
|
|
1727
|
+
});
|
|
1728
|
+
for (const [, extCol] of existingMap) {
|
|
1729
|
+
merged.push(extCol);
|
|
1730
|
+
}
|
|
1731
|
+
return merged.sort((a, b) => {
|
|
1732
|
+
var _a, _b;
|
|
1733
|
+
return ((_a = a.order) != null ? _a : 0) - ((_b = b.order) != null ? _b : 0);
|
|
1734
|
+
});
|
|
1735
|
+
}, []);
|
|
1736
|
+
const [localColumns, setLocalColumns] = (0, import_react7.useState)(() => (schema == null ? void 0 : schema.columns) || []);
|
|
1737
|
+
(0, import_react7.useEffect)(() => {
|
|
948
1738
|
if ((schema == null ? void 0 : schema.columns) && schema.columns.length > 0) {
|
|
949
|
-
setLocalColumns(schema.columns);
|
|
1739
|
+
setLocalColumns((prev) => mergeColumnDefs(prev, schema.columns));
|
|
950
1740
|
}
|
|
951
|
-
}, [schema == null ? void 0 : schema.columns]);
|
|
952
|
-
const [dirty, setDirty] = (0,
|
|
953
|
-
const dirtyRef = (0,
|
|
954
|
-
(0,
|
|
1741
|
+
}, [schema == null ? void 0 : schema.columns, mergeColumnDefs]);
|
|
1742
|
+
const [dirty, setDirty] = (0, import_react7.useState)({ added: [], modified: {}, deleted: [] });
|
|
1743
|
+
const dirtyRef = (0, import_react7.useRef)(dirty);
|
|
1744
|
+
(0, import_react7.useEffect)(() => {
|
|
955
1745
|
dirtyRef.current = dirty;
|
|
956
1746
|
}, [dirty]);
|
|
957
|
-
const mergeWithDirty = (0,
|
|
1747
|
+
const mergeWithDirty = (0, import_react7.useCallback)((baseRecords) => {
|
|
958
1748
|
const { added, modified, deleted } = dirtyRef.current;
|
|
959
1749
|
const nonDeleted = (baseRecords || []).filter((r) => !deleted.includes(r.id));
|
|
960
1750
|
const modifiedRecords = nonDeleted.map((r) => {
|
|
@@ -967,8 +1757,8 @@ function TableBlockClient({
|
|
|
967
1757
|
const uniqueAdded = added.filter((r) => !baseIds.has(r.id));
|
|
968
1758
|
return [...uniqueAdded, ...modifiedRecords];
|
|
969
1759
|
}, []);
|
|
970
|
-
const [data, setData] = (0,
|
|
971
|
-
(0,
|
|
1760
|
+
const [data, setData] = (0, import_react7.useState)(() => normalizeRecords(initialRecords));
|
|
1761
|
+
(0, import_react7.useEffect)(() => {
|
|
972
1762
|
if (initialRecords) {
|
|
973
1763
|
setData(mergeWithDirty(normalizeRecords(initialRecords)));
|
|
974
1764
|
}
|
|
@@ -983,54 +1773,83 @@ function TableBlockClient({
|
|
|
983
1773
|
}
|
|
984
1774
|
return false;
|
|
985
1775
|
};
|
|
986
|
-
const [baseRecordsMap, setBaseRecordsMap] = (0,
|
|
1776
|
+
const [baseRecordsMap, setBaseRecordsMap] = (0, import_react7.useState)(() => {
|
|
987
1777
|
const map = /* @__PURE__ */ new Map();
|
|
988
1778
|
(normalizeRecords(initialRecords) || []).forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
|
|
989
1779
|
return map;
|
|
990
1780
|
});
|
|
991
|
-
(0,
|
|
1781
|
+
(0, import_react7.useEffect)(() => {
|
|
992
1782
|
if (initialRecords && initialRecords.length > 0) {
|
|
993
1783
|
const map = /* @__PURE__ */ new Map();
|
|
994
1784
|
(normalizeRecords(initialRecords) || []).forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
|
|
995
1785
|
setBaseRecordsMap(map);
|
|
996
1786
|
}
|
|
997
1787
|
}, [initialRecords]);
|
|
998
|
-
(0,
|
|
1788
|
+
const refetchTableData = (0, import_react7.useCallback)((targetPage) => {
|
|
999
1789
|
const tableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
|
|
1000
|
-
if (!tableId || !executeAction) return;
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1790
|
+
if (!tableId || !executeAction) return Promise.resolve();
|
|
1791
|
+
const activePage = targetPage !== void 0 ? targetPage : page;
|
|
1792
|
+
const skip = (activePage - 1) * limit;
|
|
1793
|
+
return executeAction("FETCH_TABLE_DATA", { tableId, page: activePage, limit, skip }).then((res) => {
|
|
1794
|
+
var _a;
|
|
1795
|
+
if (res) {
|
|
1796
|
+
if (((_a = res.schema) == null ? void 0 : _a.columns) && res.schema.columns.length > 0) {
|
|
1797
|
+
setLocalColumns((prev) => mergeColumnDefs(prev, res.schema.columns));
|
|
1798
|
+
}
|
|
1799
|
+
if (res.records && Array.isArray(res.records)) {
|
|
1800
|
+
const norm = normalizeRecords(res.records);
|
|
1801
|
+
const map = /* @__PURE__ */ new Map();
|
|
1802
|
+
norm.forEach((r) => map.set(r.id, __spreadValues({}, r.data)));
|
|
1803
|
+
setBaseRecordsMap(map);
|
|
1804
|
+
setData(norm);
|
|
1805
|
+
}
|
|
1806
|
+
if (res.meta) {
|
|
1807
|
+
const total = typeof res.meta.totalRecords === "number" ? res.meta.totalRecords : 0;
|
|
1808
|
+
const pages = typeof res.meta.totalPages === "number" ? res.meta.totalPages : Math.ceil(total / limit) || 1;
|
|
1809
|
+
setTotalRecords(total);
|
|
1810
|
+
setTotalPages(pages);
|
|
1811
|
+
if (activePage > pages && pages >= 1) {
|
|
1812
|
+
setPage(pages);
|
|
1014
1813
|
}
|
|
1814
|
+
} else if (res.records && Array.isArray(res.records)) {
|
|
1815
|
+
setTotalRecords(res.records.length);
|
|
1816
|
+
setTotalPages(Math.ceil(res.records.length / limit) || 1);
|
|
1015
1817
|
}
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1818
|
+
}
|
|
1819
|
+
}).catch((err) => {
|
|
1820
|
+
console.error("[TableBlock] Failed to auto-fetch table data:", err);
|
|
1821
|
+
});
|
|
1822
|
+
}, [schema == null ? void 0 : schema.tableMetaId, schema == null ? void 0 : schema.tableId, schema == null ? void 0 : schema.id, page, limit, executeAction, mergeColumnDefs]);
|
|
1823
|
+
(0, import_react7.useEffect)(() => {
|
|
1824
|
+
refetchTableData();
|
|
1825
|
+
}, [refetchTableData]);
|
|
1826
|
+
const handleLimitChange = (newLimit) => {
|
|
1827
|
+
setLimit(newLimit);
|
|
1828
|
+
setPage(1);
|
|
1829
|
+
if (typeof window !== "undefined") {
|
|
1830
|
+
localStorage.setItem(`table_limit_${tableKey}`, String(newLimit));
|
|
1019
1831
|
}
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1832
|
+
};
|
|
1833
|
+
const handlePrevPage = () => {
|
|
1834
|
+
setPage((prev) => Math.max(1, prev - 1));
|
|
1835
|
+
};
|
|
1836
|
+
const handleNextPage = () => {
|
|
1837
|
+
setPage((prev) => Math.min(totalPages, prev + 1));
|
|
1838
|
+
};
|
|
1839
|
+
(0, import_react7.useEffect)(() => {
|
|
1022
1840
|
if (error) {
|
|
1023
1841
|
showError(error, "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E8\u05DB\u05D9\u05D1");
|
|
1024
1842
|
}
|
|
1025
1843
|
}, [error, showError]);
|
|
1026
|
-
const [searchQuery, setSearchQuery] = (0,
|
|
1027
|
-
const [selectedIds, setSelectedIds] = (0,
|
|
1028
|
-
const [pendingDeletions, setPendingDeletions] = (0,
|
|
1029
|
-
const [isSaving, setIsSaving] = (0,
|
|
1030
|
-
const [isSaveSuccess, setIsSaveSuccess] = (0,
|
|
1031
|
-
const [relationOptions, setRelationOptions] = (0,
|
|
1032
|
-
const
|
|
1033
|
-
const
|
|
1844
|
+
const [searchQuery, setSearchQuery] = (0, import_react7.useState)("");
|
|
1845
|
+
const [selectedIds, setSelectedIds] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1846
|
+
const [pendingDeletions, setPendingDeletions] = (0, import_react7.useState)([]);
|
|
1847
|
+
const [isSaving, setIsSaving] = (0, import_react7.useState)(false);
|
|
1848
|
+
const [isSaveSuccess, setIsSaveSuccess] = (0, import_react7.useState)(false);
|
|
1849
|
+
const [relationOptions, setRelationOptions] = (0, import_react7.useState)({});
|
|
1850
|
+
const [expandedRowIds, setExpandedRowIds] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1851
|
+
const schemaColsString = (0, import_react7.useMemo)(() => JSON.stringify(localColumns || []), [localColumns]);
|
|
1852
|
+
const refetchRelation = (0, import_react7.useCallback)(async (colName) => {
|
|
1034
1853
|
var _a;
|
|
1035
1854
|
let parsedCols = [];
|
|
1036
1855
|
try {
|
|
@@ -1050,7 +1869,7 @@ function TableBlockClient({
|
|
|
1050
1869
|
console.warn(`[TableBlock] Could not fetch relation options for column ${colName}:`, err);
|
|
1051
1870
|
}
|
|
1052
1871
|
}, [schemaColsString, executeAction]);
|
|
1053
|
-
(0,
|
|
1872
|
+
(0, import_react7.useEffect)(() => {
|
|
1054
1873
|
let parsedCols = [];
|
|
1055
1874
|
try {
|
|
1056
1875
|
parsedCols = JSON.parse(schemaColsString);
|
|
@@ -1064,10 +1883,144 @@ function TableBlockClient({
|
|
|
1064
1883
|
refetchRelation(col.name);
|
|
1065
1884
|
});
|
|
1066
1885
|
}, [schemaColsString, refetchRelation]);
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1886
|
+
const normalizedColumns = (0, import_react7.useMemo)(() => {
|
|
1887
|
+
return (localColumns || []).map((col, idx) => {
|
|
1888
|
+
const isRequired = col.isRequired === true || String(col.isRequired) === "true";
|
|
1889
|
+
const isUnique = col.isUnique === true || String(col.isUnique) === "true";
|
|
1890
|
+
const isProtected = isRequired || isUnique;
|
|
1891
|
+
return __spreadProps(__spreadValues({}, col), {
|
|
1892
|
+
order: typeof col.order === "number" ? col.order : idx,
|
|
1893
|
+
isHidden: isProtected ? false : Boolean(col.isHidden)
|
|
1894
|
+
});
|
|
1895
|
+
}).sort((a, b) => {
|
|
1896
|
+
var _a, _b;
|
|
1897
|
+
return ((_a = a.order) != null ? _a : 0) - ((_b = b.order) != null ? _b : 0);
|
|
1898
|
+
});
|
|
1070
1899
|
}, [localColumns]);
|
|
1900
|
+
const filteredColumns = (0, import_react7.useMemo)(() => {
|
|
1901
|
+
const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
|
|
1902
|
+
return normalizedColumns.filter((col) => !systemFields.includes(col.name) && !col.isHidden);
|
|
1903
|
+
}, [normalizedColumns]);
|
|
1904
|
+
const customizableColumns = (0, import_react7.useMemo)(() => {
|
|
1905
|
+
const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
|
|
1906
|
+
return normalizedColumns.filter((col) => !systemFields.includes(col.name));
|
|
1907
|
+
}, [normalizedColumns]);
|
|
1908
|
+
const [isColumnsDropdownOpen, setIsColumnsDropdownOpen] = (0, import_react7.useState)(false);
|
|
1909
|
+
const dropdownRef = (0, import_react7.useRef)(null);
|
|
1910
|
+
(0, import_react7.useEffect)(() => {
|
|
1911
|
+
const handleClickOutside = (e) => {
|
|
1912
|
+
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
1913
|
+
setIsColumnsDropdownOpen(false);
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
if (isColumnsDropdownOpen) {
|
|
1917
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1918
|
+
}
|
|
1919
|
+
return () => {
|
|
1920
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1921
|
+
};
|
|
1922
|
+
}, [isColumnsDropdownOpen]);
|
|
1923
|
+
const handleShowAllColumns = async () => {
|
|
1924
|
+
var _a;
|
|
1925
|
+
const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
|
|
1926
|
+
const systemCols = (localColumns || []).filter((c) => systemFields.includes(c.name));
|
|
1927
|
+
const updatedCustomCols = customizableColumns.map((col) => __spreadProps(__spreadValues({}, col), {
|
|
1928
|
+
isHidden: false
|
|
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 show all columns:", err);
|
|
1946
|
+
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");
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
};
|
|
1950
|
+
const handleToggleColumnVisibility = async (colName, isVisible) => {
|
|
1951
|
+
var _a;
|
|
1952
|
+
const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
|
|
1953
|
+
const systemCols = (localColumns || []).filter((c) => systemFields.includes(c.name));
|
|
1954
|
+
const updatedCustomCols = customizableColumns.map((col) => {
|
|
1955
|
+
const isRequired = col.isRequired === true || String(col.isRequired) === "true";
|
|
1956
|
+
const isUnique = col.isUnique === true || String(col.isUnique) === "true";
|
|
1957
|
+
const isProtected = isRequired || isUnique;
|
|
1958
|
+
if (col.name === colName) {
|
|
1959
|
+
if (isProtected) {
|
|
1960
|
+
return __spreadProps(__spreadValues({}, col), { isHidden: false });
|
|
1961
|
+
}
|
|
1962
|
+
return __spreadProps(__spreadValues({}, col), { isHidden: !isVisible });
|
|
1963
|
+
}
|
|
1964
|
+
return __spreadProps(__spreadValues({}, col), {
|
|
1965
|
+
isHidden: isProtected ? false : Boolean(col.isHidden)
|
|
1966
|
+
});
|
|
1967
|
+
});
|
|
1968
|
+
const fullUpdatedColumns = [...systemCols, ...updatedCustomCols];
|
|
1969
|
+
setLocalColumns(fullUpdatedColumns);
|
|
1970
|
+
const targetTableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
|
|
1971
|
+
if (targetTableId && executeAction) {
|
|
1972
|
+
try {
|
|
1973
|
+
const res = await executeAction("UPDATE_TABLE_COLUMNS", {
|
|
1974
|
+
tableId: targetTableId,
|
|
1975
|
+
tableMetaId: targetTableId,
|
|
1976
|
+
columns: fullUpdatedColumns
|
|
1977
|
+
});
|
|
1978
|
+
const returnedCols = (res == null ? void 0 : res.columns) || ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.columns);
|
|
1979
|
+
if (returnedCols && Array.isArray(returnedCols)) {
|
|
1980
|
+
setLocalColumns((prev) => mergeColumnDefs(prev, returnedCols));
|
|
1981
|
+
}
|
|
1982
|
+
} catch (err) {
|
|
1983
|
+
console.error("[TableBlock] Failed to save column visibility:", err);
|
|
1984
|
+
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");
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
const [draggedColName, setDraggedColName] = (0, import_react7.useState)(null);
|
|
1989
|
+
const [dragOverColName, setDragOverColName] = (0, import_react7.useState)(null);
|
|
1990
|
+
const handleColumnReorder = async (draggedName, dropTargetName) => {
|
|
1991
|
+
var _a;
|
|
1992
|
+
if (draggedName === dropTargetName) return;
|
|
1993
|
+
const currentCustomCols = [...customizableColumns];
|
|
1994
|
+
const fromIndex = currentCustomCols.findIndex((c) => c.name === draggedName);
|
|
1995
|
+
const toIndex = currentCustomCols.findIndex((c) => c.name === dropTargetName);
|
|
1996
|
+
if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return;
|
|
1997
|
+
const [draggedItem] = currentCustomCols.splice(fromIndex, 1);
|
|
1998
|
+
currentCustomCols.splice(toIndex, 0, draggedItem);
|
|
1999
|
+
const updatedCustomCols = currentCustomCols.map((col, idx) => __spreadProps(__spreadValues({}, col), {
|
|
2000
|
+
order: idx
|
|
2001
|
+
}));
|
|
2002
|
+
const systemFields = ["_id", "createdAt", "updatedAt", "orgId", "tableId"];
|
|
2003
|
+
const systemCols = (localColumns || []).filter((c) => systemFields.includes(c.name));
|
|
2004
|
+
const fullUpdatedColumns = [...systemCols, ...updatedCustomCols];
|
|
2005
|
+
setLocalColumns(fullUpdatedColumns);
|
|
2006
|
+
const targetTableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
|
|
2007
|
+
if (targetTableId && executeAction) {
|
|
2008
|
+
try {
|
|
2009
|
+
const res = await executeAction("UPDATE_TABLE_COLUMNS", {
|
|
2010
|
+
tableId: targetTableId,
|
|
2011
|
+
tableMetaId: targetTableId,
|
|
2012
|
+
columns: fullUpdatedColumns
|
|
2013
|
+
});
|
|
2014
|
+
const returnedCols = (res == null ? void 0 : res.columns) || ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.columns);
|
|
2015
|
+
if (returnedCols && Array.isArray(returnedCols)) {
|
|
2016
|
+
setLocalColumns((prev) => mergeColumnDefs(prev, returnedCols));
|
|
2017
|
+
}
|
|
2018
|
+
} catch (err) {
|
|
2019
|
+
console.error("[TableBlock] Failed to save column reorder:", err);
|
|
2020
|
+
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");
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
1071
2024
|
const generateTempId = () => `temp_${Math.random().toString(36).substr(2, 9)}`;
|
|
1072
2025
|
const handleAddRow = () => {
|
|
1073
2026
|
const defaultData = {};
|
|
@@ -1075,6 +2028,10 @@ function TableBlockClient({
|
|
|
1075
2028
|
var _a;
|
|
1076
2029
|
const typeUpper = (_a = col.type) == null ? void 0 : _a.toUpperCase();
|
|
1077
2030
|
let initialValue = col.defaultValue;
|
|
2031
|
+
if (typeUpper === "SUB_TABLE") {
|
|
2032
|
+
defaultData[col.name] = [];
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
1078
2035
|
if (initialValue !== void 0 && initialValue !== null && initialValue !== "") {
|
|
1079
2036
|
if (typeUpper === "CURRENCY" || typeUpper === "NUMBER") {
|
|
1080
2037
|
initialValue = parseFloat(initialValue);
|
|
@@ -1097,6 +2054,34 @@ function TableBlockClient({
|
|
|
1097
2054
|
setData((prev) => [newRow, ...prev]);
|
|
1098
2055
|
setDirty((prev) => __spreadProps(__spreadValues({}, prev), { added: [newRow, ...prev.added] }));
|
|
1099
2056
|
};
|
|
2057
|
+
const handleDuplicateRow = () => {
|
|
2058
|
+
if (selectedIds.size !== 1) return;
|
|
2059
|
+
const targetId = Array.from(selectedIds)[0];
|
|
2060
|
+
const sourceRow = data.find((r) => r.id === targetId);
|
|
2061
|
+
if (!sourceRow) return;
|
|
2062
|
+
const uniqueColNames = new Set(
|
|
2063
|
+
(localColumns || []).filter((c) => c.isUnique === true || String(c.isUnique) === "true").map((c) => c.name)
|
|
2064
|
+
);
|
|
2065
|
+
const fieldsToScrub = /* @__PURE__ */ new Set(["id", "_id", "createdAt", "updatedAt", "created_at", "updated_at"]);
|
|
2066
|
+
const clonedData = __spreadValues({}, sourceRow.data);
|
|
2067
|
+
Object.keys(clonedData).forEach((key) => {
|
|
2068
|
+
var _a;
|
|
2069
|
+
if (fieldsToScrub.has(key) || uniqueColNames.has(key)) {
|
|
2070
|
+
const col = (localColumns || []).find((c) => c.name === key);
|
|
2071
|
+
const typeUpper = (_a = col == null ? void 0 : col.type) == null ? void 0 : _a.toUpperCase();
|
|
2072
|
+
if (typeUpper === "NUMBER" || typeUpper === "CURRENCY") {
|
|
2073
|
+
clonedData[key] = 0;
|
|
2074
|
+
} else if (typeUpper === "BOOLEAN") {
|
|
2075
|
+
clonedData[key] = false;
|
|
2076
|
+
} else {
|
|
2077
|
+
clonedData[key] = "";
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
});
|
|
2081
|
+
const newRow = { id: generateTempId(), data: clonedData };
|
|
2082
|
+
setData((prev) => [newRow, ...prev]);
|
|
2083
|
+
setDirty((prev) => __spreadProps(__spreadValues({}, prev), { added: [newRow, ...prev.added] }));
|
|
2084
|
+
};
|
|
1100
2085
|
const handleCellChange = (rowId, colName, value) => {
|
|
1101
2086
|
setData((prev) => prev.map(
|
|
1102
2087
|
(row) => row.id === rowId ? __spreadProps(__spreadValues({}, row), { data: __spreadProps(__spreadValues({}, row.data), { [colName]: value }) }) : row
|
|
@@ -1130,6 +2115,36 @@ function TableBlockClient({
|
|
|
1130
2115
|
});
|
|
1131
2116
|
}
|
|
1132
2117
|
};
|
|
2118
|
+
const triggerSaveSuccess = (0, import_react7.useCallback)(() => {
|
|
2119
|
+
setIsSaveSuccess(true);
|
|
2120
|
+
setTimeout(() => {
|
|
2121
|
+
setIsSaveSuccess(false);
|
|
2122
|
+
}, 2500);
|
|
2123
|
+
}, []);
|
|
2124
|
+
const handleQuietCellChange = (0, import_react7.useCallback)((rowId, colName, value) => {
|
|
2125
|
+
setData((prev) => prev.map(
|
|
2126
|
+
(row) => row.id === rowId ? __spreadProps(__spreadValues({}, row), { data: __spreadProps(__spreadValues({}, row.data), { [colName]: value }) }) : row
|
|
2127
|
+
));
|
|
2128
|
+
if (rowId.startsWith("temp_")) {
|
|
2129
|
+
setDirty((prev) => {
|
|
2130
|
+
if (!prev.added.some((r) => r.id === rowId)) return prev;
|
|
2131
|
+
return __spreadProps(__spreadValues({}, prev), {
|
|
2132
|
+
added: prev.added.map(
|
|
2133
|
+
(r) => r.id === rowId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value }) }) : r
|
|
2134
|
+
)
|
|
2135
|
+
});
|
|
2136
|
+
});
|
|
2137
|
+
} else {
|
|
2138
|
+
setBaseRecordsMap((prev) => {
|
|
2139
|
+
const next = new Map(prev);
|
|
2140
|
+
const rowData = next.get(rowId);
|
|
2141
|
+
if (rowData) {
|
|
2142
|
+
next.set(rowId, __spreadProps(__spreadValues({}, rowData), { [colName]: value }));
|
|
2143
|
+
}
|
|
2144
|
+
return next;
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
}, []);
|
|
1133
2148
|
const toggleSelectAll = (checked) => {
|
|
1134
2149
|
const nextSelected = checked ? new Set(filteredData.map((r) => r.id)) : /* @__PURE__ */ new Set();
|
|
1135
2150
|
setSelectedIds(nextSelected);
|
|
@@ -1159,9 +2174,15 @@ function TableBlockClient({
|
|
|
1159
2174
|
const row = data.find((r) => r.id === id);
|
|
1160
2175
|
if (row) {
|
|
1161
2176
|
driveCols.forEach((col) => {
|
|
1162
|
-
const
|
|
1163
|
-
if (
|
|
1164
|
-
|
|
2177
|
+
const fileVal = row.data[col.name];
|
|
2178
|
+
if (Array.isArray(fileVal)) {
|
|
2179
|
+
fileVal.forEach((fileObj) => {
|
|
2180
|
+
if (fileObj && fileObj.fileId) {
|
|
2181
|
+
filesToDelete.push(fileObj.fileId);
|
|
2182
|
+
}
|
|
2183
|
+
});
|
|
2184
|
+
} else if (fileVal && fileVal.fileId) {
|
|
2185
|
+
filesToDelete.push(fileVal.fileId);
|
|
1165
2186
|
}
|
|
1166
2187
|
});
|
|
1167
2188
|
}
|
|
@@ -1188,7 +2209,7 @@ function TableBlockClient({
|
|
|
1188
2209
|
const formatPayload = (payload) => {
|
|
1189
2210
|
const formatted = {};
|
|
1190
2211
|
(localColumns || []).forEach((col) => {
|
|
1191
|
-
var _a;
|
|
2212
|
+
var _a, _b;
|
|
1192
2213
|
if (["_id", "id", "createdAt", "updatedAt", "orgId", "tableId", "isSelected"].includes(col.name)) return;
|
|
1193
2214
|
let val = payload[col.name];
|
|
1194
2215
|
if (val !== void 0) {
|
|
@@ -1198,6 +2219,11 @@ function TableBlockClient({
|
|
|
1198
2219
|
if (((_a = col.type) == null ? void 0 : _a.toUpperCase()) === "NUMBER") {
|
|
1199
2220
|
val = val === "" ? 0 : Number(val);
|
|
1200
2221
|
if (isNaN(val)) val = 0;
|
|
2222
|
+
} else if (((_b = col.type) == null ? void 0 : _b.toUpperCase()) === "SUB_TABLE" && Array.isArray(val)) {
|
|
2223
|
+
val = val.map((item) => {
|
|
2224
|
+
const _a2 = item, { _resolvedData } = _a2, rest = __objRest(_a2, ["_resolvedData"]);
|
|
2225
|
+
return rest;
|
|
2226
|
+
});
|
|
1201
2227
|
}
|
|
1202
2228
|
formatted[col.name] = val;
|
|
1203
2229
|
}
|
|
@@ -1317,6 +2343,7 @@ function TableBlockClient({
|
|
|
1317
2343
|
return next;
|
|
1318
2344
|
});
|
|
1319
2345
|
setDirty({ added: [], modified: {}, deleted: [] });
|
|
2346
|
+
await refetchTableData();
|
|
1320
2347
|
setIsSaveSuccess(true);
|
|
1321
2348
|
setTimeout(() => {
|
|
1322
2349
|
setIsSaveSuccess(false);
|
|
@@ -1329,7 +2356,7 @@ function TableBlockClient({
|
|
|
1329
2356
|
setIsSaving(false);
|
|
1330
2357
|
}
|
|
1331
2358
|
};
|
|
1332
|
-
const filteredData = (0,
|
|
2359
|
+
const filteredData = (0, import_react7.useMemo)(() => {
|
|
1333
2360
|
if (!searchQuery) return data;
|
|
1334
2361
|
const lowerQuery = searchQuery.toLowerCase();
|
|
1335
2362
|
return data.filter(
|
|
@@ -1340,32 +2367,60 @@ function TableBlockClient({
|
|
|
1340
2367
|
}, [data, searchQuery]);
|
|
1341
2368
|
const isUpdatingActive = Boolean(isUpdating || isSaving);
|
|
1342
2369
|
const errorMessage = error ? typeof error === "string" ? error : error.message || String(error) : null;
|
|
1343
|
-
|
|
2370
|
+
const columnTotals = (0, import_react7.useMemo)(() => {
|
|
2371
|
+
const totals = {};
|
|
2372
|
+
if (!showSummary) return totals;
|
|
2373
|
+
filteredColumns.forEach((col) => {
|
|
2374
|
+
var _a;
|
|
2375
|
+
const typeUpper = (_a = col.type) == null ? void 0 : _a.toUpperCase();
|
|
2376
|
+
if (typeUpper === "CURRENCY" || typeUpper === "NUMBER") {
|
|
2377
|
+
const sum = filteredData.reduce((acc, row) => {
|
|
2378
|
+
var _a2;
|
|
2379
|
+
const val = (_a2 = row.data) == null ? void 0 : _a2[col.name];
|
|
2380
|
+
const num = Number(val);
|
|
2381
|
+
return acc + (isNaN(num) ? 0 : num);
|
|
2382
|
+
}, 0);
|
|
2383
|
+
totals[col.name] = sum;
|
|
2384
|
+
}
|
|
2385
|
+
});
|
|
2386
|
+
return totals;
|
|
2387
|
+
}, [filteredData, filteredColumns, showSummary]);
|
|
2388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1344
2389
|
"div",
|
|
1345
2390
|
{
|
|
1346
2391
|
id: componentId,
|
|
1347
2392
|
"data-testid": dataTestId || (componentId ? `table-block-${componentId}` : void 0),
|
|
1348
|
-
className: `flex flex-col w-full bg-white border border-slate-200 rounded-xl shadow-xs overflow-hidden ${className}`,
|
|
2393
|
+
className: `flex flex-col w-full bg-white border border-slate-200 rounded-xl shadow-xs min-h-[350px] overflow-hidden ${className}`,
|
|
1349
2394
|
dir: "rtl",
|
|
1350
2395
|
children: [
|
|
1351
|
-
errorMessage && /* @__PURE__ */ (0,
|
|
1352
|
-
/* @__PURE__ */ (0,
|
|
1353
|
-
/* @__PURE__ */ (0,
|
|
2396
|
+
errorMessage && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "p-3 bg-rose-50 border-b border-rose-200 text-rose-700 text-sm flex items-center justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2397
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className: "w-4 h-4 text-rose-500 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
2398
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: errorMessage })
|
|
1354
2399
|
] }) }),
|
|
1355
|
-
/* @__PURE__ */ (0,
|
|
1356
|
-
/* @__PURE__ */ (0,
|
|
1357
|
-
((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name)) && /* @__PURE__ */ (0,
|
|
1358
|
-
/* @__PURE__ */ (0,
|
|
2400
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
|
|
2401
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-2.5", children: [
|
|
2402
|
+
((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name)) && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "font-bold text-slate-800 text-sm sm:text-base border-l border-slate-300 pl-3 ml-1", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: schema.title || schema.name }) }),
|
|
2403
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1359
2404
|
"button",
|
|
1360
2405
|
{
|
|
1361
2406
|
onClick: handleAddRow,
|
|
1362
2407
|
disabled: isLoading || isUpdatingActive,
|
|
1363
2408
|
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",
|
|
1364
2409
|
title: "\u05D4\u05D5\u05E1\u05E3 \u05E9\u05D5\u05E8\u05D4 \u05D7\u05D3\u05E9\u05D4",
|
|
1365
|
-
children: /* @__PURE__ */ (0,
|
|
2410
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className: "w-4.5 h-4.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M12 4v16m8-8H4" }) })
|
|
1366
2411
|
}
|
|
1367
2412
|
),
|
|
1368
|
-
/* @__PURE__ */ (0,
|
|
2413
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2414
|
+
"button",
|
|
2415
|
+
{
|
|
2416
|
+
onClick: handleDuplicateRow,
|
|
2417
|
+
disabled: isLoading || isUpdatingActive || selectedIds.size !== 1,
|
|
2418
|
+
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",
|
|
2419
|
+
title: "\u05E9\u05DB\u05E4\u05DC \u05E9\u05D5\u05E8\u05D4 \u05E0\u05D1\u05D7\u05E8\u05EA",
|
|
2420
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Copy, { className: "w-4 h-4 text-blue-600 shrink-0" })
|
|
2421
|
+
}
|
|
2422
|
+
),
|
|
2423
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1369
2424
|
"button",
|
|
1370
2425
|
{
|
|
1371
2426
|
onClick: handleDeleteSelected,
|
|
@@ -1373,14 +2428,120 @@ function TableBlockClient({
|
|
|
1373
2428
|
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",
|
|
1374
2429
|
title: "\u05DE\u05D7\u05E7 \u05E9\u05D5\u05E8\u05D5\u05EA \u05E0\u05D1\u05D7\u05E8\u05D5\u05EA",
|
|
1375
2430
|
children: [
|
|
1376
|
-
/* @__PURE__ */ (0,
|
|
1377
|
-
selectedIds.size > 0 && /* @__PURE__ */ (0,
|
|
2431
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className: "w-4.5 h-4.5 text-rose-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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" }) }),
|
|
2432
|
+
selectedIds.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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 })
|
|
1378
2433
|
]
|
|
1379
2434
|
}
|
|
1380
2435
|
),
|
|
1381
|
-
/* @__PURE__ */ (0,
|
|
1382
|
-
|
|
1383
|
-
|
|
2436
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2437
|
+
"button",
|
|
2438
|
+
{
|
|
2439
|
+
onClick: toggleSummary,
|
|
2440
|
+
disabled: isLoading || isUpdatingActive,
|
|
2441
|
+
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"}`,
|
|
2442
|
+
title: "\u05D4\u05E6\u05D2/\u05D4\u05E1\u05EA\u05E8 \u05E9\u05D5\u05E8\u05EA \u05E1\u05D9\u05DB\u05D5\u05DD",
|
|
2443
|
+
children: [
|
|
2444
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className: "w-4 h-4 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M4 6h16M4 10h16M4 14h16M4 18h16" }) }),
|
|
2445
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "hidden sm:inline", children: "\u05E1\u05DB\u05D9\u05DE\u05D4" })
|
|
2446
|
+
]
|
|
2447
|
+
}
|
|
2448
|
+
),
|
|
2449
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative", ref: dropdownRef, children: [
|
|
2450
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2451
|
+
"button",
|
|
2452
|
+
{
|
|
2453
|
+
onClick: () => setIsColumnsDropdownOpen((prev) => !prev),
|
|
2454
|
+
disabled: isLoading || isUpdatingActive,
|
|
2455
|
+
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",
|
|
2456
|
+
title: "\u05D4\u05E6\u05D2/\u05D4\u05E1\u05EA\u05E8 \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA",
|
|
2457
|
+
children: [
|
|
2458
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className: "w-4 h-4 text-slate-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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" }) }),
|
|
2459
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M19 9l-7 7-7-7" }) })
|
|
2460
|
+
]
|
|
2461
|
+
}
|
|
2462
|
+
),
|
|
2463
|
+
isColumnsDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
|
|
2464
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
|
|
2465
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
2466
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u05D4\u05E6\u05D2\u05EA \u05E2\u05DE\u05D5\u05D3\u05D5\u05EA" }),
|
|
2467
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "text-[10px] font-normal text-slate-400", children: [
|
|
2468
|
+
"(",
|
|
2469
|
+
customizableColumns.filter((c) => !c.isHidden).length,
|
|
2470
|
+
"/",
|
|
2471
|
+
customizableColumns.length,
|
|
2472
|
+
")"
|
|
2473
|
+
] })
|
|
2474
|
+
] }),
|
|
2475
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2476
|
+
"button",
|
|
2477
|
+
{
|
|
2478
|
+
type: "button",
|
|
2479
|
+
onClick: handleShowAllColumns,
|
|
2480
|
+
className: "text-xs text-brand hover:text-brand-hover font-semibold hover:underline cursor-pointer",
|
|
2481
|
+
children: "\u05D4\u05E6\u05D2 \u05D4\u05DB\u05DC"
|
|
2482
|
+
}
|
|
2483
|
+
)
|
|
2484
|
+
] }),
|
|
2485
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "max-h-60 overflow-y-auto space-y-0.5", children: customizableColumns.map((col) => {
|
|
2486
|
+
const isRequired = col.isRequired === true || String(col.isRequired) === "true";
|
|
2487
|
+
const isUnique = col.isUnique === true || String(col.isUnique) === "true";
|
|
2488
|
+
const isProtected = isRequired || isUnique;
|
|
2489
|
+
const isVisible = !col.isHidden;
|
|
2490
|
+
let protectedExplanation = "";
|
|
2491
|
+
if (isRequired && isUnique) {
|
|
2492
|
+
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";
|
|
2493
|
+
} else if (isRequired) {
|
|
2494
|
+
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";
|
|
2495
|
+
} else if (isUnique) {
|
|
2496
|
+
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";
|
|
2497
|
+
}
|
|
2498
|
+
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";
|
|
2499
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2500
|
+
"div",
|
|
2501
|
+
{
|
|
2502
|
+
onClick: () => {
|
|
2503
|
+
if (!isProtected) {
|
|
2504
|
+
handleToggleColumnVisibility(col.name, !isVisible);
|
|
2505
|
+
}
|
|
2506
|
+
},
|
|
2507
|
+
title: isProtected ? protectedExplanation : void 0,
|
|
2508
|
+
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"}`,
|
|
2509
|
+
children: [
|
|
2510
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2512
|
+
"input",
|
|
2513
|
+
{
|
|
2514
|
+
type: "checkbox",
|
|
2515
|
+
checked: isVisible,
|
|
2516
|
+
disabled: isProtected,
|
|
2517
|
+
onChange: () => {
|
|
2518
|
+
},
|
|
2519
|
+
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"
|
|
2520
|
+
}
|
|
2521
|
+
),
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: `font-medium truncate ${isProtected ? "text-slate-500" : "text-slate-700"}`, children: col.name })
|
|
2523
|
+
] }),
|
|
2524
|
+
isProtected && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2525
|
+
"div",
|
|
2526
|
+
{
|
|
2527
|
+
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",
|
|
2528
|
+
title: protectedExplanation,
|
|
2529
|
+
children: [
|
|
2530
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsx)("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" }) }),
|
|
2531
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: badgeLabel })
|
|
2532
|
+
]
|
|
2533
|
+
}
|
|
2534
|
+
)
|
|
2535
|
+
]
|
|
2536
|
+
},
|
|
2537
|
+
col.name
|
|
2538
|
+
);
|
|
2539
|
+
}) })
|
|
2540
|
+
] })
|
|
2541
|
+
] }),
|
|
2542
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "h-5 w-px bg-slate-300 mx-1 hidden sm:block" }),
|
|
2543
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative flex-1 sm:w-64 max-w-xs", children: [
|
|
2544
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1384
2545
|
"input",
|
|
1385
2546
|
{
|
|
1386
2547
|
placeholder: "\u05D7\u05D9\u05E4\u05D5\u05E9 \u05D1\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA...",
|
|
@@ -1390,36 +2551,45 @@ function TableBlockClient({
|
|
|
1390
2551
|
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"
|
|
1391
2552
|
}
|
|
1392
2553
|
),
|
|
1393
|
-
|
|
2554
|
+
searchQuery ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2555
|
+
"button",
|
|
2556
|
+
{
|
|
2557
|
+
type: "button",
|
|
2558
|
+
onClick: () => setSearchQuery(""),
|
|
2559
|
+
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",
|
|
2560
|
+
title: "Clear search",
|
|
2561
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.X, { className: "w-3.5 h-3.5" })
|
|
2562
|
+
}
|
|
2563
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) })
|
|
1394
2564
|
] })
|
|
1395
2565
|
] }),
|
|
1396
|
-
/* @__PURE__ */ (0,
|
|
1397
|
-
isUpdatingActive ? /* @__PURE__ */ (0,
|
|
1398
|
-
/* @__PURE__ */ (0,
|
|
2566
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
2567
|
+
isUpdatingActive ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
|
|
2568
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-2 h-2 rounded-full bg-blue-500 inline-block" }),
|
|
1399
2569
|
"\u05DE\u05E2\u05D3\u05DB\u05DF..."
|
|
1400
|
-
] }) : isSaveSuccess ? /* @__PURE__ */ (0,
|
|
1401
|
-
/* @__PURE__ */ (0,
|
|
2570
|
+
] }) : isSaveSuccess ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
|
|
2571
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className: "w-4 h-4 text-emerald-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M5 13l4 4L19 7" }) }),
|
|
1402
2572
|
"\u05E2\u05D5\u05D3\u05DB\u05DF \u05D1\u05D4\u05E6\u05DC\u05D7\u05D4"
|
|
1403
|
-
] }) : hasChanges ? /* @__PURE__ */ (0,
|
|
1404
|
-
/* @__PURE__ */ (0,
|
|
2573
|
+
] }) : hasChanges ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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,
|
|
2574
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1405
2575
|
"button",
|
|
1406
2576
|
{
|
|
1407
2577
|
onClick: handleSave,
|
|
1408
2578
|
disabled: isUpdatingActive || isLoading || !hasChanges,
|
|
1409
2579
|
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"}`,
|
|
1410
|
-
children: /* @__PURE__ */ (0,
|
|
2580
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: isUpdatingActive ? "\u05E9\u05D5\u05DE\u05E8..." : "\u05E9\u05DE\u05D9\u05E8\u05EA \u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD" })
|
|
1411
2581
|
}
|
|
1412
2582
|
)
|
|
1413
2583
|
] })
|
|
1414
2584
|
] }),
|
|
1415
|
-
/* @__PURE__ */ (0,
|
|
1416
|
-
isLoading && /* @__PURE__ */ (0,
|
|
1417
|
-
/* @__PURE__ */ (0,
|
|
1418
|
-
/* @__PURE__ */ (0,
|
|
2585
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `overflow-x-auto relative min-h-[260px] flex-1 transition-opacity duration-200 ${isMounted ? "opacity-100" : "opacity-0"}`, children: [
|
|
2586
|
+
isLoading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "absolute inset-0 bg-white/80 backdrop-blur-xs z-20 flex flex-col items-center justify-center gap-2", children: [
|
|
2587
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "w-6 h-6 border-2 border-brand border-t-transparent rounded-full animate-spin" }),
|
|
2588
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-sm font-medium text-slate-600", children: "\u05D8\u05D5\u05E2\u05DF \u05E0\u05EA\u05D5\u05E0\u05D9\u05DD..." })
|
|
1419
2589
|
] }),
|
|
1420
|
-
/* @__PURE__ */ (0,
|
|
1421
|
-
/* @__PURE__ */ (0,
|
|
1422
|
-
/* @__PURE__ */ (0,
|
|
2590
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("table", { className: "w-full text-sm text-right border-collapse table-auto", children: [
|
|
2591
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { className: "bg-slate-100/80 text-slate-700 sticky top-0 z-10", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("tr", { children: [
|
|
2592
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsx)(
|
|
1423
2593
|
"input",
|
|
1424
2594
|
{
|
|
1425
2595
|
type: "checkbox",
|
|
@@ -1429,67 +2599,313 @@ function TableBlockClient({
|
|
|
1429
2599
|
className: "w-4 h-4 rounded border-slate-300 text-brand"
|
|
1430
2600
|
}
|
|
1431
2601
|
) }),
|
|
1432
|
-
filteredColumns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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))
|
|
1433
|
-
] }) }),
|
|
1434
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tbody", { className: "bg-white", children: (isStateEmpty || filteredData.length === 0) && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { colSpan: filteredColumns.length + 1, className: "text-center py-12 text-slate-400", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col items-center justify-center gap-2", children: [
|
|
1435
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-8 h-8 text-slate-300", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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" }) }),
|
|
1436
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05DC\u05D4\u05E6\u05D2\u05D4." })
|
|
1437
|
-
] }) }) }) : filteredData.map((row) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { className: "hover:bg-blue-50/30 transition-colors group", children: [
|
|
1438
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "border-b border-l border-slate-200 p-1.5 text-center bg-white group-hover:bg-transparent", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1439
|
-
"input",
|
|
1440
|
-
{
|
|
1441
|
-
type: "checkbox",
|
|
1442
|
-
checked: selectedIds.has(row.id),
|
|
1443
|
-
onChange: (e) => toggleSelectRow(row.id, e.target.checked),
|
|
1444
|
-
disabled: isLoading || isUpdatingActive,
|
|
1445
|
-
className: "w-4 h-4 rounded border-slate-300 text-brand"
|
|
1446
|
-
}
|
|
1447
|
-
) }),
|
|
1448
2602
|
filteredColumns.map((col) => {
|
|
2603
|
+
const isDragging = draggedColName === col.name;
|
|
2604
|
+
const isDragOver = dragOverColName === col.name;
|
|
2605
|
+
const currentWidth = columnWidths[col.name] || 200;
|
|
2606
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2607
|
+
"th",
|
|
2608
|
+
{
|
|
2609
|
+
style: { width: currentWidth, minWidth: currentWidth, maxWidth: currentWidth },
|
|
2610
|
+
draggable: !isLoading && !isUpdatingActive,
|
|
2611
|
+
onDragStart: (e) => {
|
|
2612
|
+
e.dataTransfer.setData("text/plain", col.name);
|
|
2613
|
+
e.dataTransfer.effectAllowed = "move";
|
|
2614
|
+
setDraggedColName(col.name);
|
|
2615
|
+
},
|
|
2616
|
+
onDragOver: (e) => {
|
|
2617
|
+
e.preventDefault();
|
|
2618
|
+
e.dataTransfer.dropEffect = "move";
|
|
2619
|
+
if (dragOverColName !== col.name) {
|
|
2620
|
+
setDragOverColName(col.name);
|
|
2621
|
+
}
|
|
2622
|
+
},
|
|
2623
|
+
onDragLeave: () => {
|
|
2624
|
+
if (dragOverColName === col.name) {
|
|
2625
|
+
setDragOverColName(null);
|
|
2626
|
+
}
|
|
2627
|
+
},
|
|
2628
|
+
onDrop: (e) => {
|
|
2629
|
+
e.preventDefault();
|
|
2630
|
+
if (draggedColName && draggedColName !== col.name) {
|
|
2631
|
+
handleColumnReorder(draggedColName, col.name);
|
|
2632
|
+
}
|
|
2633
|
+
setDraggedColName(null);
|
|
2634
|
+
setDragOverColName(null);
|
|
2635
|
+
},
|
|
2636
|
+
onDragEnd: () => {
|
|
2637
|
+
setDraggedColName(null);
|
|
2638
|
+
setDragOverColName(null);
|
|
2639
|
+
},
|
|
2640
|
+
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"}`,
|
|
2641
|
+
children: [
|
|
2642
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2643
|
+
"div",
|
|
2644
|
+
{
|
|
2645
|
+
onMouseDown: (e) => {
|
|
2646
|
+
e.preventDefault();
|
|
2647
|
+
e.stopPropagation();
|
|
2648
|
+
setResizingCol({ name: col.name, startWidth: currentWidth, startX: e.clientX });
|
|
2649
|
+
},
|
|
2650
|
+
className: "absolute left-0 top-0 bottom-0 w-1.5 cursor-col-resize hover:bg-brand active:bg-brand-hover z-20",
|
|
2651
|
+
title: "\u05E9\u05E0\u05D4 \u05D2\u05D5\u05D3\u05DC \u05E2\u05DE\u05D5\u05D3\u05D4"
|
|
2652
|
+
}
|
|
2653
|
+
),
|
|
2654
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
|
|
2655
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate", children: col.name }),
|
|
2656
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2657
|
+
"svg",
|
|
2658
|
+
{
|
|
2659
|
+
className: "w-3.5 h-3.5 text-slate-400 opacity-40 group-hover:opacity-100 transition-opacity shrink-0 cursor-grab",
|
|
2660
|
+
fill: "none",
|
|
2661
|
+
viewBox: "0 0 24 24",
|
|
2662
|
+
stroke: "currentColor",
|
|
2663
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M8 9h.01M8 15h.01M16 9h.01M16 15h.01" })
|
|
2664
|
+
}
|
|
2665
|
+
)
|
|
2666
|
+
] })
|
|
2667
|
+
]
|
|
2668
|
+
},
|
|
2669
|
+
col.name
|
|
2670
|
+
);
|
|
2671
|
+
}),
|
|
2672
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("th", { className: "w-full min-w-[50px] border-b border-slate-200 pointer-events-none" })
|
|
2673
|
+
] }) }),
|
|
2674
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tbody", { className: "bg-white", children: (isStateEmpty || filteredData.length === 0) && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { colSpan: filteredColumns.length + 2, className: "text-center py-12 text-slate-400", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col items-center justify-center gap-2", children: [
|
|
2675
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className: "w-8 h-8 text-slate-300", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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" }) }),
|
|
2676
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05DC\u05D4\u05E6\u05D2\u05D4." })
|
|
2677
|
+
] }) }) }) : filteredData.map((row) => {
|
|
2678
|
+
const hasSubTableCols = filteredColumns.some((c) => {
|
|
1449
2679
|
var _a;
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
2680
|
+
return ((_a = c.type) == null ? void 0 : _a.toUpperCase()) === "SUB_TABLE";
|
|
2681
|
+
});
|
|
2682
|
+
const isExpanded = expandedRowIds.has(row.id);
|
|
2683
|
+
const subTableColumns = filteredColumns.filter((c) => {
|
|
2684
|
+
var _a;
|
|
2685
|
+
return ((_a = c.type) == null ? void 0 : _a.toUpperCase()) === "SUB_TABLE";
|
|
2686
|
+
});
|
|
2687
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react7.default.Fragment, { children: [
|
|
2688
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("tr", { className: "hover:bg-blue-50/30 transition-colors group", children: [
|
|
2689
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-b border-l border-slate-200 p-1.5 text-center bg-white group-hover:bg-transparent", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
2690
|
+
hasSubTableCols && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2691
|
+
"button",
|
|
2692
|
+
{
|
|
2693
|
+
type: "button",
|
|
2694
|
+
onClick: () => setExpandedRowIds((prev) => {
|
|
2695
|
+
const next = new Set(prev);
|
|
2696
|
+
if (next.has(row.id)) next.delete(row.id);
|
|
2697
|
+
else next.add(row.id);
|
|
2698
|
+
return next;
|
|
2699
|
+
}),
|
|
2700
|
+
className: "text-slate-400 hover:text-blue-600 p-0.5 rounded transition-colors cursor-pointer",
|
|
2701
|
+
title: isExpanded ? "\u05E1\u05D2\u05D5\u05E8 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4" : "\u05E4\u05EA\u05D7 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4",
|
|
2702
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M9 5l7 7-7 7" }) })
|
|
2703
|
+
}
|
|
2704
|
+
),
|
|
2705
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2706
|
+
"input",
|
|
2707
|
+
{
|
|
2708
|
+
type: "checkbox",
|
|
2709
|
+
checked: selectedIds.has(row.id),
|
|
2710
|
+
onChange: (e) => toggleSelectRow(row.id, e.target.checked),
|
|
2711
|
+
disabled: isLoading || isUpdatingActive,
|
|
2712
|
+
className: "w-4 h-4 rounded border-slate-300 text-brand"
|
|
2713
|
+
}
|
|
2714
|
+
)
|
|
2715
|
+
] }) }),
|
|
2716
|
+
filteredColumns.map((col) => {
|
|
2717
|
+
var _a;
|
|
2718
|
+
const colType = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
|
|
2719
|
+
const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
|
|
2720
|
+
let value = (row == null ? void 0 : row.data) ? row.data[col.name] : void 0;
|
|
2721
|
+
if (value === void 0 && col.defaultValue !== void 0) {
|
|
2722
|
+
value = colType === "CURRENCY" || colType === "NUMBER" ? parseFloat(col.defaultValue) : col.defaultValue;
|
|
2723
|
+
if (colType === "CURRENCY" || colType === "NUMBER") {
|
|
2724
|
+
if (isNaN(value)) value = null;
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
if (value === void 0) value = null;
|
|
2728
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-b border-l border-slate-200 p-0 relative", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2729
|
+
InputComponent,
|
|
2730
|
+
{
|
|
2731
|
+
value,
|
|
2732
|
+
column: col,
|
|
2733
|
+
rowId: row.id,
|
|
2734
|
+
onChange: (v) => handleCellChange(row.id, col.name, v),
|
|
2735
|
+
context: {
|
|
2736
|
+
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",
|
|
2737
|
+
tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
|
|
2738
|
+
onMarkForDeletion: (fileId) => setPendingDeletions((prev) => [...prev, fileId]),
|
|
2739
|
+
relationOptions,
|
|
2740
|
+
refetchRelation,
|
|
2741
|
+
executeAction,
|
|
2742
|
+
emitEvent,
|
|
2743
|
+
pageState,
|
|
2744
|
+
componentId,
|
|
2745
|
+
orgId,
|
|
2746
|
+
updateQuietly: handleQuietCellChange,
|
|
2747
|
+
triggerSaveSuccess,
|
|
2748
|
+
rowData: row.data
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
) }, col.name);
|
|
2752
|
+
}),
|
|
2753
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-b border-slate-200 bg-white pointer-events-none" })
|
|
2754
|
+
] }),
|
|
2755
|
+
isExpanded && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2756
|
+
ExpandedSubTables,
|
|
1462
2757
|
{
|
|
1463
|
-
value,
|
|
1464
|
-
column: col,
|
|
1465
2758
|
rowId: row.id,
|
|
1466
|
-
|
|
2759
|
+
rowData: row.data,
|
|
2760
|
+
subTableColumns,
|
|
2761
|
+
filteredColumnsLength: filteredColumns.length,
|
|
2762
|
+
handleCellChange,
|
|
1467
2763
|
context: {
|
|
1468
|
-
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",
|
|
1469
|
-
onMarkForDeletion: (fileId) => setPendingDeletions((prev) => [...prev, fileId]),
|
|
1470
|
-
relationOptions,
|
|
1471
|
-
refetchRelation,
|
|
1472
2764
|
executeAction,
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
2765
|
+
orgId,
|
|
2766
|
+
parentTableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
|
|
2767
|
+
tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
|
|
2768
|
+
rowData: row.data,
|
|
2769
|
+
updateQuietly: handleQuietCellChange,
|
|
2770
|
+
triggerSaveSuccess
|
|
1477
2771
|
}
|
|
1478
2772
|
}
|
|
1479
|
-
)
|
|
1480
|
-
})
|
|
1481
|
-
|
|
2773
|
+
)
|
|
2774
|
+
] }, row.id);
|
|
2775
|
+
}) }),
|
|
2776
|
+
showSummary && filteredData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsxs)("tr", { children: [
|
|
2777
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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' }),
|
|
2778
|
+
filteredColumns.map((col) => {
|
|
2779
|
+
var _a;
|
|
2780
|
+
const typeUpper = (_a = col.type) == null ? void 0 : _a.toUpperCase();
|
|
2781
|
+
const isNumeric = typeUpper === "CURRENCY" || typeUpper === "NUMBER";
|
|
2782
|
+
const total = columnTotals[col.name];
|
|
2783
|
+
let displayVal = "";
|
|
2784
|
+
if (isNumeric && total !== void 0) {
|
|
2785
|
+
displayVal = new Intl.NumberFormat("en-US").format(total);
|
|
2786
|
+
if (typeUpper === "CURRENCY") {
|
|
2787
|
+
const symbol = getCurrencySymbol(col.currencySymbol);
|
|
2788
|
+
displayVal = symbol ? `${symbol} ${displayVal}` : displayVal;
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex items-center text-right justify-start", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate", children: displayVal }) }) }, col.name);
|
|
2792
|
+
}),
|
|
2793
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-b border-slate-200 bg-slate-50 pointer-events-none" })
|
|
2794
|
+
] }) })
|
|
2795
|
+
] })
|
|
2796
|
+
] }),
|
|
2797
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
|
|
2798
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2799
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-medium text-slate-700", children: "\u05E9\u05D5\u05E8\u05D5\u05EA \u05DC\u05E2\u05DE\u05D5\u05D3:" }),
|
|
2800
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2801
|
+
"select",
|
|
2802
|
+
{
|
|
2803
|
+
value: limit,
|
|
2804
|
+
onChange: (e) => handleLimitChange(Number(e.target.value)),
|
|
2805
|
+
disabled: isLoading || isUpdatingActive,
|
|
2806
|
+
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",
|
|
2807
|
+
children: [
|
|
2808
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: 20, children: "20" }),
|
|
2809
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: 50, children: "50" }),
|
|
2810
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: 100, children: "100" })
|
|
2811
|
+
]
|
|
2812
|
+
}
|
|
2813
|
+
)
|
|
2814
|
+
] }),
|
|
2815
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-4", children: [
|
|
2816
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-medium text-slate-600", children: [
|
|
2817
|
+
"\u05E2\u05DE\u05D5\u05D3 ",
|
|
2818
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { className: "text-slate-900", children: page }),
|
|
2819
|
+
" \u05DE\u05EA\u05D5\u05DA ",
|
|
2820
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { className: "text-slate-900", children: totalPages || 1 }),
|
|
2821
|
+
totalRecords > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "text-slate-500 font-normal", children: [
|
|
2822
|
+
" (",
|
|
2823
|
+
totalRecords,
|
|
2824
|
+
' \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05D1\u05E1\u05D4"\u05DB)'
|
|
2825
|
+
] })
|
|
2826
|
+
] }),
|
|
2827
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
2828
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2829
|
+
"button",
|
|
2830
|
+
{
|
|
2831
|
+
onClick: handlePrevPage,
|
|
2832
|
+
disabled: page <= 1 || isLoading || isUpdatingActive,
|
|
2833
|
+
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",
|
|
2834
|
+
title: "\u05E2\u05DE\u05D5\u05D3 \u05E7\u05D5\u05D3\u05DD",
|
|
2835
|
+
children: "\u05D4\u05E7\u05D5\u05D3\u05DD"
|
|
2836
|
+
}
|
|
2837
|
+
),
|
|
2838
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2839
|
+
"button",
|
|
2840
|
+
{
|
|
2841
|
+
onClick: handleNextPage,
|
|
2842
|
+
disabled: page >= totalPages || isLoading || isUpdatingActive,
|
|
2843
|
+
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",
|
|
2844
|
+
title: "\u05E2\u05DE\u05D5\u05D3 \u05D4\u05D1\u05D0",
|
|
2845
|
+
children: "\u05D4\u05D1\u05D0"
|
|
2846
|
+
}
|
|
2847
|
+
)
|
|
2848
|
+
] })
|
|
1482
2849
|
] })
|
|
1483
2850
|
] })
|
|
1484
2851
|
]
|
|
1485
2852
|
}
|
|
1486
2853
|
);
|
|
1487
2854
|
}
|
|
2855
|
+
function ExpandedSubTables({
|
|
2856
|
+
rowId,
|
|
2857
|
+
rowData,
|
|
2858
|
+
subTableColumns,
|
|
2859
|
+
filteredColumnsLength,
|
|
2860
|
+
handleCellChange,
|
|
2861
|
+
context
|
|
2862
|
+
}) {
|
|
2863
|
+
var _a;
|
|
2864
|
+
const [activeTab, setActiveTab] = (0, import_react7.useState)((_a = subTableColumns[0]) == null ? void 0 : _a.name);
|
|
2865
|
+
if (subTableColumns.length === 0) return null;
|
|
2866
|
+
if (subTableColumns.length === 1) {
|
|
2867
|
+
const stCol = subTableColumns[0];
|
|
2868
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { colSpan: filteredColumnsLength + 2, className: "p-0 border-b border-slate-200 bg-slate-50/50", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2869
|
+
SubTableViewer,
|
|
2870
|
+
{
|
|
2871
|
+
rowId,
|
|
2872
|
+
column: stCol,
|
|
2873
|
+
value: (rowData == null ? void 0 : rowData[stCol.name]) || [],
|
|
2874
|
+
onChange: (newVal) => handleCellChange(rowId, stCol.name, newVal),
|
|
2875
|
+
context
|
|
2876
|
+
}
|
|
2877
|
+
) }) });
|
|
2878
|
+
}
|
|
2879
|
+
const activeCol = subTableColumns.find((c) => c.name === activeTab) || subTableColumns[0];
|
|
2880
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("td", { colSpan: filteredColumnsLength + 2, className: "p-0 border-b border-slate-200 bg-slate-50/50 overflow-hidden", children: [
|
|
2881
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsx)(
|
|
2882
|
+
"button",
|
|
2883
|
+
{
|
|
2884
|
+
type: "button",
|
|
2885
|
+
onClick: () => setActiveTab(col.name),
|
|
2886
|
+
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"}`,
|
|
2887
|
+
children: col.name
|
|
2888
|
+
},
|
|
2889
|
+
col.name
|
|
2890
|
+
)) }),
|
|
2891
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2892
|
+
SubTableViewer,
|
|
2893
|
+
{
|
|
2894
|
+
rowId,
|
|
2895
|
+
column: activeCol,
|
|
2896
|
+
value: (rowData == null ? void 0 : rowData[activeCol.name]) || [],
|
|
2897
|
+
onChange: (newVal) => handleCellChange(rowId, activeCol.name, newVal),
|
|
2898
|
+
context
|
|
2899
|
+
},
|
|
2900
|
+
activeCol.name
|
|
2901
|
+
) })
|
|
2902
|
+
] }) });
|
|
2903
|
+
}
|
|
1488
2904
|
|
|
1489
2905
|
// src/components/TableBlock/TableBlock.tsx
|
|
1490
|
-
var
|
|
2906
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1491
2907
|
function TableBlock(props) {
|
|
1492
|
-
return /* @__PURE__ */ (0,
|
|
2908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableBlockClient, __spreadValues({}, props));
|
|
1493
2909
|
}
|
|
1494
2910
|
|
|
1495
2911
|
// src/registry.ts
|