@universe-platform/ui 0.1.0-beta.11 → 0.1.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -99,6 +99,30 @@ function getDeterministicPastelBadge(key) {
99
99
  return PASTEL_PALETTES[index];
100
100
  }
101
101
 
102
+ // src/utils/permissions.ts
103
+ function getRolePermissions(role) {
104
+ if (role === "VIEWER" || !role) {
105
+ return {
106
+ permissions: {
107
+ canCreate: false,
108
+ canRead: true,
109
+ canUpdate: false,
110
+ canDelete: false
111
+ },
112
+ readOnly: true
113
+ };
114
+ }
115
+ return {
116
+ permissions: {
117
+ canCreate: true,
118
+ canRead: true,
119
+ canUpdate: true,
120
+ canDelete: true
121
+ },
122
+ readOnly: false
123
+ };
124
+ }
125
+
102
126
  // ../shared/src/columns.ts
103
127
  var COLUMN_TYPES = [
104
128
  "STRING",
@@ -376,7 +400,7 @@ import React7 from "react";
376
400
  import React2, { useState as useState2, useEffect } from "react";
377
401
  import { FileText, Upload, X as X2 } from "lucide-react";
378
402
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
379
- function DrivePickerCell({ value, onChange, tableName, column, rowId, onMarkForDeletion, context }) {
403
+ function DrivePickerCell({ value, onChange, tableName, column, rowId, onMarkForDeletion, context, readOnly = false }) {
380
404
  const [isOpening, setIsOpening] = useState2(false);
381
405
  const { showError } = useToast();
382
406
  const cellContextId = React2.useMemo(() => {
@@ -477,29 +501,33 @@ function DrivePickerCell({ value, onChange, tableName, column, rowId, onMarkForD
477
501
  const updated = fileList.filter((_, idx) => idx !== indexToRemove);
478
502
  onChange(updated);
479
503
  };
480
- const renderUploadButton = () => /* @__PURE__ */ jsx2("div", { className: "flex items-center w-full h-8 px-2", children: /* @__PURE__ */ jsxs2(
481
- "button",
482
- {
483
- type: "button",
484
- onClick: openUploadPopup,
485
- disabled: isOpening || !(context == null ? void 0 : context.executeAction),
486
- className: "inline-flex items-center gap-1.5 px-2 py-1 rounded-md text-xs font-medium text-slate-500 bg-slate-100 hover:bg-slate-200 hover:text-slate-700 transition-colors disabled:opacity-50",
487
- children: [
488
- /* @__PURE__ */ jsx2(Upload, { className: "w-3.5 h-3.5" }),
489
- /* @__PURE__ */ jsx2("span", { children: isOpening ? "Loading..." : "Upload File" })
490
- ]
491
- }
492
- ) });
504
+ const renderUploadButton = (iconOnly) => {
505
+ if (readOnly) return null;
506
+ return /* @__PURE__ */ jsxs2(
507
+ "button",
508
+ {
509
+ type: "button",
510
+ onClick: openUploadPopup,
511
+ disabled: isOpening || !(context == null ? void 0 : context.executeAction),
512
+ className: `inline-flex items-center justify-center gap-1.5 rounded-md text-xs font-medium text-slate-500 bg-slate-100 hover:bg-slate-200 hover:text-slate-700 transition-colors disabled:opacity-50 ${iconOnly ? "w-7 h-7 shrink-0" : "px-2 py-1"}`,
513
+ title: "Upload File",
514
+ children: [
515
+ /* @__PURE__ */ jsx2(Upload, { className: "w-3.5 h-3.5 shrink-0" }),
516
+ !iconOnly && /* @__PURE__ */ jsx2("span", { className: "truncate", children: isOpening ? "Loading..." : "Upload File" })
517
+ ]
518
+ }
519
+ );
520
+ };
493
521
  if (fileList.length > 0) {
494
- return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col w-full py-1 gap-1", children: [
495
- fileList.map((file, idx) => /* @__PURE__ */ jsxs2("div", { className: "flex items-center w-full h-8 px-2 gap-1", children: [
522
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center w-full py-1 px-2 gap-1.5", children: [
523
+ fileList.map((file, idx) => /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1 max-w-full", children: [
496
524
  /* @__PURE__ */ jsxs2(
497
525
  "a",
498
526
  {
499
527
  href: file.fileUrl || file.url,
500
528
  target: "_blank",
501
529
  rel: "noopener noreferrer",
502
- 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",
530
+ className: `inline-flex items-center gap-1.5 px-2 py-1 rounded-md text-xs font-medium text-blue-700 bg-blue-50 hover:bg-blue-100 transition-colors truncate ${readOnly ? "max-w-full" : "max-w-[calc(100%-28px)]"}`,
503
531
  title: file.fileName || "Open file",
504
532
  children: [
505
533
  /* @__PURE__ */ jsx2(FileText, { className: "w-3.5 h-3.5 shrink-0" }),
@@ -507,21 +535,24 @@ function DrivePickerCell({ value, onChange, tableName, column, rowId, onMarkForD
507
535
  ]
508
536
  }
509
537
  ),
510
- /* @__PURE__ */ jsx2(
538
+ !readOnly && /* @__PURE__ */ jsx2(
511
539
  "button",
512
540
  {
513
541
  type: "button",
514
542
  onClick: () => handleRemoveFile(idx),
515
- className: "shrink-0 p-0.5 rounded hover:bg-rose-100 text-slate-400 hover:text-rose-500 transition-colors",
543
+ className: "shrink-0 inline-flex items-center justify-center w-6 h-6 rounded hover:bg-rose-100 text-slate-400 hover:text-rose-500 transition-colors",
516
544
  title: "Remove file",
517
- children: /* @__PURE__ */ jsx2(X2, { className: "w-3.5 h-3.5" })
545
+ children: /* @__PURE__ */ jsx2(X2, { className: "w-3.5 h-3.5 shrink-0" })
518
546
  }
519
547
  )
520
548
  ] }, file.fileId || idx)),
521
- renderUploadButton()
549
+ renderUploadButton(true)
522
550
  ] });
523
551
  }
524
- return renderUploadButton();
552
+ if (readOnly) {
553
+ return /* @__PURE__ */ jsx2("span", { className: "px-2 py-1 flex items-center h-full text-slate-300 text-xs italic", children: "-" });
554
+ }
555
+ return /* @__PURE__ */ jsx2("div", { className: "flex items-center w-full h-8 px-2", children: renderUploadButton(false) });
525
556
  }
526
557
 
527
558
  // src/components/TableBlock/fields/DatePickerCell.tsx
@@ -2088,11 +2119,19 @@ function getCurrencySymbol(code) {
2088
2119
  return CURRENCY_SYMBOLS[code.toUpperCase()] || code;
2089
2120
  }
2090
2121
  var FORM_INPUTS = {
2091
- DATE: ({ value, onChange, column }) => /* @__PURE__ */ jsx7(DatePickerCell, { value, onChange, column }),
2092
- CURRENCY: ({ value, column, onChange }) => {
2122
+ DATE: ({ value, onChange, column, readOnly }) => /* @__PURE__ */ jsx7(DatePickerCell, { value, onChange, column, readOnly }),
2123
+ CURRENCY: ({ value, column, onChange, readOnly }) => {
2093
2124
  var _a;
2094
2125
  const symbol = getCurrencySymbol((_a = column.config) == null ? void 0 : _a.currencySymbol);
2095
2126
  const [isFocused, setIsFocused] = React7.useState(false);
2127
+ if (readOnly) {
2128
+ const num = Number(value);
2129
+ const formattedValue = value !== null && value !== void 0 && !isNaN(num) ? new Intl.NumberFormat("en-US").format(num) : "";
2130
+ return /* @__PURE__ */ jsxs7("span", { className: "px-2 py-1 flex items-center h-full text-sm select-text truncate", children: [
2131
+ symbol && /* @__PURE__ */ jsx7("span", { className: "text-slate-400 mr-1", children: symbol }),
2132
+ formattedValue
2133
+ ] });
2134
+ }
2096
2135
  const displayValue = !isFocused && value !== void 0 && value !== null && !isNaN(Number(value)) ? new Intl.NumberFormat("en-US").format(Number(value)) : value !== void 0 && value !== null ? value : "";
2097
2136
  return /* @__PURE__ */ jsxs7("div", { className: "relative w-full h-full", children: [
2098
2137
  symbol && /* @__PURE__ */ jsx7("span", { className: "absolute left-2 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none text-sm", children: symbol }),
@@ -2113,18 +2152,19 @@ var FORM_INPUTS = {
2113
2152
  )
2114
2153
  ] });
2115
2154
  },
2116
- TIME: ({ value, onChange, column }) => /* @__PURE__ */ jsx7(TimePickerCell, { value, onChange, column }),
2117
- LINK: ({ value, onChange, column }) => /* @__PURE__ */ jsx7(LinkPickerCell, { value, onChange, column }),
2118
- BOOLEAN: ({ value, onChange }) => /* @__PURE__ */ jsx7("div", { className: "flex items-center justify-center w-full h-full p-2", children: /* @__PURE__ */ jsx7(
2155
+ TIME: ({ value, onChange, column, readOnly }) => /* @__PURE__ */ jsx7(TimePickerCell, { value, onChange, column, readOnly }),
2156
+ LINK: ({ value, onChange, column, readOnly }) => /* @__PURE__ */ jsx7(LinkPickerCell, { value, onChange, column, readOnly }),
2157
+ BOOLEAN: ({ value, onChange, readOnly }) => /* @__PURE__ */ jsx7("div", { className: "flex items-center justify-center w-full h-full p-2", children: /* @__PURE__ */ jsx7(
2119
2158
  "input",
2120
2159
  {
2121
2160
  type: "checkbox",
2122
2161
  checked: !!value,
2162
+ disabled: readOnly,
2123
2163
  onChange: (e) => onChange(e.target.checked),
2124
- className: "w-4 h-4 rounded border-slate-300 text-brand"
2164
+ className: `w-4 h-4 rounded border-slate-300 text-brand ${readOnly ? "cursor-not-allowed opacity-70" : "cursor-pointer"}`
2125
2165
  }
2126
2166
  ) }),
2127
- GOOGLE_DRIVE_FILE: ({ value, onChange, column, rowId, context }) => /* @__PURE__ */ jsx7(
2167
+ GOOGLE_DRIVE_FILE: ({ value, onChange, column, rowId, context, readOnly }) => /* @__PURE__ */ jsx7(
2128
2168
  DrivePickerCell,
2129
2169
  {
2130
2170
  value: value || [],
@@ -2133,34 +2173,60 @@ var FORM_INPUTS = {
2133
2173
  rowId,
2134
2174
  tableName: context == null ? void 0 : context.tableName,
2135
2175
  onMarkForDeletion: context == null ? void 0 : context.onMarkForDeletion,
2136
- context
2176
+ context,
2177
+ readOnly
2137
2178
  }
2138
2179
  ),
2139
- RELATION: ({ value, onChange, column, context, rowId }) => /* @__PURE__ */ jsx7(
2180
+ RELATION: ({ value, onChange, column, context, rowId, readOnly }) => /* @__PURE__ */ jsx7(
2140
2181
  RelationPickerCell,
2141
2182
  {
2142
2183
  value,
2143
2184
  onChange,
2144
2185
  column,
2145
2186
  context,
2146
- rowId
2187
+ rowId,
2188
+ readOnly
2147
2189
  }
2148
2190
  ),
2149
- NUMBER: ({ value, onChange }) => /* @__PURE__ */ jsx7(
2150
- "input",
2151
- {
2152
- type: "number",
2153
- className: "w-full h-8 px-2 bg-transparent focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 text-sm",
2154
- value: value !== void 0 && value !== null ? value : "",
2155
- onChange: (e) => {
2156
- const val = e.target.value;
2157
- onChange(val === "" ? null : Number(val));
2158
- }
2191
+ NUMBER: ({ value, onChange, readOnly }) => {
2192
+ if (readOnly) {
2193
+ return /* @__PURE__ */ jsx7("span", { className: "px-2 py-1 flex items-center h-full text-sm select-text truncate", children: value !== void 0 && value !== null ? value : "" });
2159
2194
  }
2160
- ),
2161
- PHONE: ({ value, onChange, column }) => {
2195
+ return /* @__PURE__ */ jsx7(
2196
+ "input",
2197
+ {
2198
+ type: "number",
2199
+ className: "w-full h-8 px-2 bg-transparent focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 text-sm",
2200
+ value: value !== void 0 && value !== null ? value : "",
2201
+ onChange: (e) => {
2202
+ const val = e.target.value;
2203
+ onChange(val === "" ? null : Number(val));
2204
+ }
2205
+ }
2206
+ );
2207
+ },
2208
+ PHONE: ({ value, onChange, column, readOnly }) => {
2162
2209
  var _a;
2163
2210
  const enableWhatsApp = (_a = column == null ? void 0 : column.config) == null ? void 0 : _a.enableWhatsApp;
2211
+ const formatted = value ? formatPhoneDisplay(String(value)) : "";
2212
+ if (readOnly) {
2213
+ return /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between w-full h-full px-2 gap-1.5 overflow-hidden", children: [
2214
+ /* @__PURE__ */ jsx7("span", { className: "truncate text-sm", dir: "ltr", children: formatted }),
2215
+ enableWhatsApp && value && /* @__PURE__ */ jsx7(
2216
+ "button",
2217
+ {
2218
+ type: "button",
2219
+ className: "text-emerald-500 cursor-pointer hover:opacity-80 transition-opacity shrink-0 p-1 rounded hover:bg-emerald-50",
2220
+ title: "\u05E4\u05EA\u05D9\u05D7\u05EA \u05D5\u05D5\u05D0\u05D8\u05E1\u05D0\u05E4",
2221
+ onClick: (e) => {
2222
+ e.stopPropagation();
2223
+ openWhatsAppChat(String(value));
2224
+ },
2225
+ children: /* @__PURE__ */ jsx7("svg", { className: "w-4 h-4", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx7("path", { d: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51a5.816 5.816 0 00-.571-.01c-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" }) })
2226
+ }
2227
+ )
2228
+ ] });
2229
+ }
2164
2230
  return /* @__PURE__ */ jsxs7("div", { className: "flex items-center w-full h-full px-2 gap-1.5 overflow-hidden", children: [
2165
2231
  /* @__PURE__ */ jsx7(
2166
2232
  "input",
@@ -2187,85 +2253,9 @@ var FORM_INPUTS = {
2187
2253
  )
2188
2254
  ] });
2189
2255
  },
2190
- GOOGLE_CONTACT: ({ value, column, rowId, onChange, context }) => {
2256
+ GOOGLE_CONTACT: ({ value, column, rowId, onChange, context, readOnly }) => {
2191
2257
  const [isSyncing, setIsSyncing] = React7.useState(false);
2192
2258
  const { addToast } = useToast();
2193
- const googleClientIdRef = React7.useRef(null);
2194
- const ensureGisScript = React7.useCallback(() => {
2195
- return new Promise((resolve, reject) => {
2196
- var _a, _b, _c;
2197
- if ((_c = (_b = (_a = window.google) == null ? void 0 : _a.accounts) == null ? void 0 : _b.oauth2) == null ? void 0 : _c.initTokenClient) {
2198
- resolve();
2199
- return;
2200
- }
2201
- if (document.querySelector('script[src*="accounts.google.com/gsi/client"]')) {
2202
- const waitForLoad = setInterval(() => {
2203
- var _a2, _b2, _c2;
2204
- if ((_c2 = (_b2 = (_a2 = window.google) == null ? void 0 : _a2.accounts) == null ? void 0 : _b2.oauth2) == null ? void 0 : _c2.initTokenClient) {
2205
- clearInterval(waitForLoad);
2206
- resolve();
2207
- }
2208
- }, 100);
2209
- setTimeout(() => {
2210
- clearInterval(waitForLoad);
2211
- reject(new Error("GIS script load timeout"));
2212
- }, 1e4);
2213
- return;
2214
- }
2215
- const script = document.createElement("script");
2216
- script.src = "https://accounts.google.com/gsi/client";
2217
- script.async = true;
2218
- script.onload = () => resolve();
2219
- script.onerror = () => reject(new Error("Failed to load Google Identity Services"));
2220
- document.head.appendChild(script);
2221
- });
2222
- }, []);
2223
- const requestGisToken = React7.useCallback(async (hint) => {
2224
- await ensureGisScript();
2225
- if (!googleClientIdRef.current && (context == null ? void 0 : context.executeAction)) {
2226
- try {
2227
- const config = await context.executeAction("GET_GOOGLE_CONFIG", {});
2228
- googleClientIdRef.current = (config == null ? void 0 : config.clientId) || "";
2229
- } catch (e) {
2230
- throw new Error("Failed to fetch Google configuration");
2231
- }
2232
- }
2233
- const clientId = googleClientIdRef.current;
2234
- if (!clientId) {
2235
- throw new Error("Google Client ID is not configured");
2236
- }
2237
- const accessToken = await new Promise((resolve, reject) => {
2238
- const tokenClient = window.google.accounts.oauth2.initTokenClient({
2239
- client_id: clientId,
2240
- scope: "https://www.googleapis.com/auth/contacts https://www.googleapis.com/auth/userinfo.email",
2241
- prompt: hint ? "" : "select_account",
2242
- hint: hint || void 0,
2243
- callback: (tokenResponse) => {
2244
- if (tokenResponse == null ? void 0 : tokenResponse.access_token) {
2245
- resolve(tokenResponse.access_token);
2246
- } else {
2247
- reject(new Error("Google authorization was cancelled or failed"));
2248
- }
2249
- },
2250
- error_callback: (err) => {
2251
- reject(new Error((err == null ? void 0 : err.message) || "Google authorization error"));
2252
- }
2253
- });
2254
- tokenClient.requestAccessToken({ hint: hint || void 0 });
2255
- });
2256
- let email = "";
2257
- try {
2258
- const res = await fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
2259
- headers: { Authorization: `Bearer ${accessToken}` }
2260
- });
2261
- if (res.ok) {
2262
- const info = await res.json();
2263
- email = info.email || "";
2264
- }
2265
- } catch (e) {
2266
- }
2267
- return { accessToken, email };
2268
- }, [ensureGisScript, context]);
2269
2259
  const handleSync = async () => {
2270
2260
  var _a, _b, _c, _d, _e, _f;
2271
2261
  if (!(context == null ? void 0 : context.executeAction)) return;
@@ -2300,17 +2290,6 @@ var FORM_INPUTS = {
2300
2290
  setIsSyncing(false);
2301
2291
  return;
2302
2292
  }
2303
- let accessToken;
2304
- let userEmail = "";
2305
- try {
2306
- const gisResult = await requestGisToken();
2307
- accessToken = gisResult.accessToken;
2308
- userEmail = gisResult.email;
2309
- } catch (gisError) {
2310
- addToast({ type: "warning", message: gisError.message || "\u05D4\u05D4\u05E8\u05E9\u05D0\u05D4 \u05DC\u05D2\u05D5\u05D2\u05DC \u05E0\u05DB\u05E9\u05DC\u05D4" });
2311
- setIsSyncing(false);
2312
- return;
2313
- }
2314
2293
  const payload = {
2315
2294
  nameParts: mapping.nameParts || [],
2316
2295
  phoneCol: mapping.phoneCol,
@@ -2320,9 +2299,7 @@ var FORM_INPUTS = {
2320
2299
  tableMetaId: targetTableMetaId,
2321
2300
  tableId: targetTableMetaId,
2322
2301
  recordId: rowId,
2323
- orgId: targetOrgId,
2324
- accessToken,
2325
- userEmail
2302
+ orgId: targetOrgId
2326
2303
  };
2327
2304
  const res = await context.executeAction("SYNC_GOOGLE_CONTACT", payload);
2328
2305
  if (res && res.synced) {
@@ -2342,39 +2319,44 @@ var FORM_INPUTS = {
2342
2319
  }
2343
2320
  }
2344
2321
  } catch (error) {
2345
- console.error("Failed to sync contact:", error);
2346
- addToast({ type: "warning", message: "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E1\u05E0\u05DB\u05E8\u05D5\u05DF \u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8: " + error.message });
2322
+ const errorMessage = (error == null ? void 0 : error.message) || (error == null ? void 0 : error.error) || (typeof error === "string" ? error : "");
2323
+ if (errorMessage.includes("OAUTH_REQUIRED") || errorMessage.includes("Access Denied") || errorMessage.includes("No refresh token")) {
2324
+ addToast({
2325
+ type: "warning",
2326
+ message: "\u05D8\u05E8\u05DD \u05D4\u05D5\u05D2\u05D3\u05E8 \u05D7\u05E9\u05D1\u05D5\u05DF \u05D2\u05D5\u05D2\u05DC \u05D0\u05E8\u05D2\u05D5\u05E0\u05D9. \u05D9\u05E9 \u05DC\u05E4\u05E0\u05D5\u05EA \u05DC\u05DE\u05E0\u05D4\u05DC \u05D4\u05DE\u05E2\u05E8\u05DB\u05EA \u05DC\u05D7\u05D9\u05D1\u05D5\u05E8 \u05D4\u05D7\u05E9\u05D1\u05D5\u05DF \u05D1\u05D4\u05D2\u05D3\u05E8\u05D5\u05EA \u05D4\u05D0\u05E8\u05D2\u05D5\u05DF."
2327
+ });
2328
+ } else {
2329
+ console.error("Failed to sync contact:", error);
2330
+ addToast({
2331
+ type: "warning",
2332
+ message: "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E1\u05E0\u05DB\u05E8\u05D5\u05DF \u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8: " + error.message
2333
+ });
2334
+ }
2347
2335
  } finally {
2348
2336
  setIsSyncing(false);
2349
2337
  }
2350
2338
  };
2351
2339
  const handleUnlink = async () => {
2352
- let accessToken;
2353
2340
  const targetTableMetaId = (context == null ? void 0 : context.tableMetaId) || (context == null ? void 0 : context.tableId);
2354
2341
  const targetOrgId = context == null ? void 0 : context.orgId;
2355
2342
  try {
2356
- let emailHint = value == null ? void 0 : value.userEmail;
2357
- if (!emailHint && (value == null ? void 0 : value.url)) {
2358
- try {
2359
- const parsed = new URL(value.url);
2360
- emailHint = parsed.searchParams.get("authuser") || void 0;
2361
- } catch (e) {
2362
- }
2343
+ if ((value == null ? void 0 : value.resourceName) && (context == null ? void 0 : context.executeAction)) {
2344
+ await context.executeAction("DELETE_GOOGLE_CONTACT", {
2345
+ resourceName: value.resourceName,
2346
+ tableMetaId: targetTableMetaId,
2347
+ tableId: targetTableMetaId,
2348
+ recordId: rowId,
2349
+ orgId: targetOrgId
2350
+ });
2363
2351
  }
2364
- const auth = await requestGisToken(emailHint);
2365
- accessToken = auth.accessToken;
2366
- } catch (err) {
2367
- console.warn("[handleUnlink] Could not obtain token via GIS, falling back to server default token:", err);
2368
- }
2369
- if ((value == null ? void 0 : value.resourceName) && (context == null ? void 0 : context.executeAction)) {
2370
- await context.executeAction("DELETE_GOOGLE_CONTACT", {
2371
- resourceName: value.resourceName,
2372
- accessToken,
2373
- tableMetaId: targetTableMetaId,
2374
- tableId: targetTableMetaId,
2375
- recordId: rowId,
2376
- orgId: targetOrgId
2377
- });
2352
+ } catch (error) {
2353
+ if (error.message === "OAUTH_REQUIRED" || error.error === "OAUTH_REQUIRED") {
2354
+ addToast({ type: "warning", message: "\u05D8\u05E8\u05DD \u05D4\u05D5\u05D2\u05D3\u05E8 \u05D7\u05E9\u05D1\u05D5\u05DF \u05D2\u05D5\u05D2\u05DC \u05D0\u05E8\u05D2\u05D5\u05E0\u05D9. \u05D9\u05E9 \u05DC\u05E4\u05E0\u05D5\u05EA \u05DC\u05DE\u05E0\u05D4\u05DC \u05D4\u05DE\u05E2\u05E8\u05DB\u05EA \u05DC\u05D7\u05D9\u05D1\u05D5\u05E8 \u05D4\u05D7\u05E9\u05D1\u05D5\u05DF \u05D1\u05D4\u05D2\u05D3\u05E8\u05D5\u05EA \u05D4\u05D0\u05E8\u05D2\u05D5\u05DF." });
2355
+ return;
2356
+ }
2357
+ console.error("Failed to unlink contact:", error);
2358
+ addToast({ type: "warning", message: "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D4\u05E1\u05E8\u05EA \u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8: " + error.message });
2359
+ return;
2378
2360
  }
2379
2361
  if (context == null ? void 0 : context.updateQuietly) {
2380
2362
  context.updateQuietly(rowId, column.name, null);
@@ -2394,7 +2376,7 @@ var FORM_INPUTS = {
2394
2376
  if (value && value.synced && value.url) {
2395
2377
  return /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1.5 h-8 px-2 group whitespace-nowrap overflow-hidden max-w-full", children: [
2396
2378
  /* @__PURE__ */ jsx7("a", { href: value.url, target: "_blank", rel: "noopener noreferrer", className: "text-blue-600 hover:underline flex-1 truncate text-xs whitespace-nowrap", children: "\u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8 \u05D2\u05D5\u05D2\u05DC" }),
2397
- /* @__PURE__ */ jsx7(
2379
+ !readOnly && /* @__PURE__ */ jsx7(
2398
2380
  "button",
2399
2381
  {
2400
2382
  type: "button",
@@ -2406,6 +2388,9 @@ var FORM_INPUTS = {
2406
2388
  )
2407
2389
  ] });
2408
2390
  }
2391
+ if (readOnly) {
2392
+ return /* @__PURE__ */ jsx7("span", { className: "px-2 py-1 flex items-center h-full text-slate-300 text-xs italic", children: "\u05DC\u05D0 \u05DE\u05E1\u05D5\u05E0\u05DB\u05E8\u05DF" });
2393
+ }
2409
2394
  return /* @__PURE__ */ jsx7("div", { className: "flex items-center h-8 px-2 whitespace-nowrap overflow-hidden max-w-full", children: /* @__PURE__ */ jsxs7(
2410
2395
  "button",
2411
2396
  {
@@ -2423,15 +2408,20 @@ var FORM_INPUTS = {
2423
2408
  }
2424
2409
  ) });
2425
2410
  },
2426
- DEFAULT: ({ value, onChange }) => /* @__PURE__ */ jsx7(
2427
- "input",
2428
- {
2429
- type: "text",
2430
- className: "w-full h-8 px-2 bg-transparent focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 text-sm",
2431
- value: value !== void 0 && value !== null ? String(value) : "",
2432
- onChange: (e) => onChange(e.target.value)
2411
+ DEFAULT: ({ value, onChange, readOnly }) => {
2412
+ if (readOnly) {
2413
+ return /* @__PURE__ */ jsx7("span", { className: "px-2 py-1 flex items-center h-full text-sm select-text truncate", children: value !== void 0 && value !== null ? String(value) : "" });
2433
2414
  }
2434
- ),
2415
+ return /* @__PURE__ */ jsx7(
2416
+ "input",
2417
+ {
2418
+ type: "text",
2419
+ className: "w-full h-8 px-2 bg-transparent focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 text-sm",
2420
+ value: value !== void 0 && value !== null ? String(value) : "",
2421
+ onChange: (e) => onChange(e.target.value)
2422
+ }
2423
+ );
2424
+ },
2435
2425
  SUB_TABLE: ({ value, column }) => {
2436
2426
  var _a, _b, _c;
2437
2427
  if (!Array.isArray(value)) return /* @__PURE__ */ jsx7("span", { className: "px-2 py-1 flex items-center h-full text-slate-300 text-xs italic", children: "\u05E8\u05D9\u05E7" });
@@ -2841,7 +2831,8 @@ function SubTableCell({
2841
2831
  refetchRelation,
2842
2832
  onCommit,
2843
2833
  onQuietUpdate,
2844
- width
2834
+ width,
2835
+ readOnly
2845
2836
  }) {
2846
2837
  var _a, _b, _c, _d, _e;
2847
2838
  const colType = ((_a = column.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
@@ -2876,6 +2867,7 @@ function SubTableCell({
2876
2867
  }
2877
2868
  }, [serverValue]);
2878
2869
  const commit = useCallback8(async (valToCommit) => {
2870
+ if (readOnly) return true;
2879
2871
  if (isCommittingRef.current) return false;
2880
2872
  if (!isDirtyRef.current) return true;
2881
2873
  if (valToCommit === serverValueRef.current) {
@@ -2992,7 +2984,9 @@ function SubTableCell({
2992
2984
  column,
2993
2985
  rowId: record.id,
2994
2986
  onChange: handleChange,
2987
+ readOnly,
2995
2988
  context: __spreadProps(__spreadValues({}, context), {
2989
+ readOnly,
2996
2990
  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",
2997
2991
  relationOptions,
2998
2992
  updateRelationOptions: (name, opts) => setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [name]: opts })),
@@ -3022,8 +3016,9 @@ function SubTableCell({
3022
3016
  }
3023
3017
  );
3024
3018
  }
3025
- function SubTableViewer({ rowId, column, value, onChange, context }) {
3019
+ function SubTableViewer({ rowId, column, value, onChange, context, readOnly }) {
3026
3020
  var _a, _b, _c, _d, _e, _f;
3021
+ const isReadOnly = readOnly != null ? readOnly : Boolean(context == null ? void 0 : context.readOnly);
3027
3022
  const { addToast } = useToast();
3028
3023
  const entries = Array.isArray(value) ? value : [];
3029
3024
  const [linkedRecords, setLinkedRecords] = useState10(() => {
@@ -3550,7 +3545,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3550
3545
  return /* @__PURE__ */ jsxs9("div", { className: "p-3 animate-in fade-in slide-in-from-top-2 duration-200", dir: "rtl", children: [
3551
3546
  /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-4 mb-2.5", children: [
3552
3547
  /* @__PURE__ */ jsx9("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx9("span", { className: "text-xs font-bold text-slate-600", children: (_e = targetSchema == null ? void 0 : targetSchema.name) != null ? _e : (_d = column.config) == null ? void 0 : _d.referencedTableName }) }),
3553
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5", children: [
3548
+ !isReadOnly && /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5", children: [
3554
3549
  /* @__PURE__ */ jsxs9("div", { className: "relative", ref: searchRef, children: [
3555
3550
  /* @__PURE__ */ jsxs9(
3556
3551
  "button",
@@ -3615,13 +3610,13 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3615
3610
  )
3616
3611
  ] })
3617
3612
  ] }),
3618
- isLoading ? /* @__PURE__ */ jsx9("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx9("div", { className: "w-5 h-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" }) }) : entries.length === 0 && !isCreating ? /* @__PURE__ */ jsx9("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__ */ jsx9("div", { className: "overflow-x-auto pb-1", children: /* @__PURE__ */ jsxs9("table", { className: "w-full text-xs border-collapse table-auto", children: [
3613
+ isLoading ? /* @__PURE__ */ jsx9("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx9("div", { className: "w-5 h-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" }) }) : entries.length === 0 && !isCreating ? /* @__PURE__ */ jsx9("div", { className: "text-center py-6 text-xs text-slate-400", children: isReadOnly ? "\u05D0\u05D9\u05DF \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05DE\u05E7\u05D5\u05E9\u05E8\u05D5\u05EA \u05DC\u05D4\u05E6\u05D2\u05D4." : "\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__ */ jsx9("div", { className: "overflow-x-auto pb-1", children: /* @__PURE__ */ jsxs9("table", { className: "w-full text-xs border-collapse table-auto", children: [
3619
3614
  /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs9("tr", { className: "bg-slate-100/80 h-12 min-h-[48px]", children: [
3620
3615
  /* @__PURE__ */ jsx9("th", { className: "border border-slate-200 px-2 py-1.5 text-center w-10 font-semibold text-slate-500 h-12 min-h-[48px] align-middle", style: { width: 40, minWidth: 40, maxWidth: 40 }, children: "\u05E0\u05D1\u05D7\u05E8" }),
3621
3616
  targetColumns.map((col) => {
3622
- const dragProps = getHeaderDragProps(col.name);
3623
- const isDragging = draggedColName === col.name;
3624
- const isDragOver = dragOverColName === col.name;
3617
+ const dragProps = isReadOnly ? {} : getHeaderDragProps(col.name);
3618
+ const isDragging = !isReadOnly && draggedColName === col.name;
3619
+ const isDragOver = !isReadOnly && dragOverColName === col.name;
3625
3620
  const colMinWidth = calculateMinColumnWidth(col.name, true, col.type);
3626
3621
  const currentWidth = Math.max(colMinWidth, columnWidths[col.name] || 150);
3627
3622
  return /* @__PURE__ */ jsxs9(
@@ -3630,7 +3625,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3630
3625
  style: { width: currentWidth, minWidth: currentWidth, maxWidth: currentWidth },
3631
3626
  title: col.name
3632
3627
  }, dragProps), {
3633
- className: `relative border border-slate-200 px-3 py-1.5 font-semibold text-slate-600 select-none cursor-grab active:cursor-grabbing transition-colors h-12 min-h-[48px] align-middle ${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"}`,
3628
+ className: `relative border border-slate-200 px-3 py-1.5 font-semibold text-slate-600 select-none ${isReadOnly ? "cursor-default" : "cursor-grab active:cursor-grabbing"} transition-colors h-12 min-h-[48px] align-middle ${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" : isReadOnly ? "" : "hover:bg-slate-200/60"}`,
3634
3629
  children: [
3635
3630
  /* @__PURE__ */ jsx9(
3636
3631
  "div",
@@ -3646,7 +3641,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3646
3641
  ),
3647
3642
  /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between gap-1.5 group overflow-hidden w-full", title: col.name, children: [
3648
3643
  /* @__PURE__ */ jsx9(ColumnHeaderTitle, { name: col.name, width: currentWidth, isSubTable: true }),
3649
- /* @__PURE__ */ jsx9(
3644
+ !isReadOnly && /* @__PURE__ */ jsx9(
3650
3645
  "svg",
3651
3646
  {
3652
3647
  className: "w-3.5 h-3.5 text-slate-400 opacity-40 group-hover:opacity-100 transition-opacity shrink-0 cursor-grab",
@@ -3675,8 +3670,9 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3675
3670
  {
3676
3671
  type: "checkbox",
3677
3672
  checked: entry.isSelected,
3673
+ disabled: isReadOnly,
3678
3674
  onChange: () => handleToggleSelected(entry.recordId),
3679
- className: "w-3.5 h-3.5 rounded border-slate-300 text-blue-600 cursor-pointer"
3675
+ className: `w-3.5 h-3.5 rounded border-slate-300 text-blue-600 ${isReadOnly ? "cursor-not-allowed opacity-60" : "cursor-pointer"}`
3680
3676
  }
3681
3677
  ) }),
3682
3678
  targetColumns.map((col) => {
@@ -3706,7 +3702,8 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3706
3702
  refetchRelation,
3707
3703
  onCommit: handleCommitRecordCell,
3708
3704
  onQuietUpdate: handleQuietSubTableCellChange,
3709
- width: currentWidth
3705
+ width: currentWidth,
3706
+ readOnly: isReadOnly
3710
3707
  },
3711
3708
  `${record.id}_${col.name}`
3712
3709
  );
@@ -3733,7 +3730,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3733
3730
  }
3734
3731
  return null;
3735
3732
  })(),
3736
- /* @__PURE__ */ jsx9(
3733
+ !isReadOnly && /* @__PURE__ */ jsx9(
3737
3734
  "button",
3738
3735
  {
3739
3736
  type: "button",
@@ -3750,7 +3747,7 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
3750
3747
  /* @__PURE__ */ jsx9("td", { className: "border border-slate-200 bg-white pointer-events-none" })
3751
3748
  ] }, entry.recordId);
3752
3749
  }),
3753
- isCreating && /* @__PURE__ */ jsxs9("tr", { className: "bg-emerald-50/40", children: [
3750
+ isCreating && !isReadOnly && /* @__PURE__ */ jsxs9("tr", { className: "bg-emerald-50/40", children: [
3754
3751
  /* @__PURE__ */ jsx9("td", { className: "border border-emerald-200 px-2 py-1 text-center", style: { width: 40, minWidth: 40, maxWidth: 40 }, children: /* @__PURE__ */ jsx9("span", { className: "text-emerald-500 font-bold text-[10px]", children: "\u05D7\u05D3\u05E9" }) }),
3755
3752
  targetColumns.map((col) => {
3756
3753
  var _a2, _b2, _c2, _d2, _e2;
@@ -4265,10 +4262,21 @@ function TableBlockClient({
4265
4262
  componentId,
4266
4263
  orgId,
4267
4264
  className = "",
4268
- "data-testid": dataTestId
4265
+ "data-testid": dataTestId,
4266
+ role,
4267
+ permissions,
4268
+ readOnly
4269
4269
  }) {
4270
4270
  const { showError, addToast } = useToast();
4271
4271
  const [isExporting, setIsExporting] = useState12(false);
4272
+ const resolvedRole = role ? getRolePermissions(role) : void 0;
4273
+ const effectivePermissions = permissions || (resolvedRole == null ? void 0 : resolvedRole.permissions) || {
4274
+ canCreate: !readOnly,
4275
+ canRead: true,
4276
+ canUpdate: !readOnly,
4277
+ canDelete: !readOnly
4278
+ };
4279
+ const effectiveReadOnly = readOnly != null ? readOnly : resolvedRole ? resolvedRole.readOnly : !effectivePermissions.canUpdate && !effectivePermissions.canCreate && !effectivePermissions.canDelete;
4272
4280
  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";
4273
4281
  const { columnWidths, startResize } = useColumnResize({
4274
4282
  storageKey: `table_widths_${tableKey}`,
@@ -4279,6 +4287,10 @@ function TableBlockClient({
4279
4287
  return calculateMinColumnWidth(colName, false, col == null ? void 0 : col.type);
4280
4288
  }
4281
4289
  });
4290
+ const executeActionRef = useRef8(executeAction);
4291
+ useEffect10(() => {
4292
+ executeActionRef.current = executeAction;
4293
+ }, [executeAction]);
4282
4294
  const [isMounted, setIsMounted] = useState12(false);
4283
4295
  const [isInitialLoading, setIsInitialLoading] = useState12(true);
4284
4296
  const [page, setPage] = useState12(1);
@@ -4381,6 +4393,7 @@ function TableBlockClient({
4381
4393
  }, []);
4382
4394
  const [data, setData] = useState12(() => normalizeRecords(initialRecords));
4383
4395
  const [serverRecords, setServerRecords] = useState12(() => normalizeRecords(initialRecords));
4396
+ const filteredData = data;
4384
4397
  useEffect10(() => {
4385
4398
  if (initialRecords) {
4386
4399
  const norm = normalizeRecords(initialRecords);
@@ -4418,6 +4431,8 @@ function TableBlockClient({
4418
4431
  const [relationTriggerRect, setRelationTriggerRect] = useState12(null);
4419
4432
  const filterButtonRefs = useRef8({});
4420
4433
  const [relationOptions, setRelationOptions] = useState12({});
4434
+ const [relationFilterOptions, setRelationFilterOptions] = useState12({});
4435
+ const [loadingFilterCol, setLoadingFilterCol] = useState12(null);
4421
4436
  const relationDictionaryRef = useRef8({});
4422
4437
  const mergeRelationOptions = useCallback10((colName, newOptions) => {
4423
4438
  if (!Array.isArray(newOptions) || newOptions.length === 0) return;
@@ -4482,7 +4497,8 @@ function TableBlockClient({
4482
4497
  const fetchRequestIdRef = useRef8(0);
4483
4498
  const fetchTableData = useCallback10((targetPage, targetLimit, targetSearch, targetFiltersKey) => {
4484
4499
  const tableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
4485
- if (!tableId || !executeAction) return Promise.resolve();
4500
+ const action = executeActionRef.current;
4501
+ if (!tableId || !action) return Promise.resolve();
4486
4502
  const currentRequestId = ++fetchRequestIdRef.current;
4487
4503
  setIsFetching(true);
4488
4504
  let activeFilters = {};
@@ -4491,7 +4507,7 @@ function TableBlockClient({
4491
4507
  } catch (e) {
4492
4508
  }
4493
4509
  const skip = (targetPage - 1) * targetLimit;
4494
- return executeAction("FETCH_TABLE_DATA", {
4510
+ return action("FETCH_TABLE_DATA", {
4495
4511
  tableId,
4496
4512
  page: targetPage,
4497
4513
  limit: targetLimit,
@@ -4540,7 +4556,7 @@ function TableBlockClient({
4540
4556
  setIsInitialLoading(false);
4541
4557
  }
4542
4558
  });
4543
- }, [schema == null ? void 0 : schema.tableMetaId, schema == null ? void 0 : schema.tableId, schema == null ? void 0 : schema.id, executeAction, mergeColumnDefs, mergeWithDirty, mergeRelationOptions]);
4559
+ }, [schema == null ? void 0 : schema.tableMetaId, schema == null ? void 0 : schema.tableId, schema == null ? void 0 : schema.id, mergeColumnDefs, mergeWithDirty, mergeRelationOptions]);
4544
4560
  useEffect10(() => {
4545
4561
  fetchTableData(page, limit, debouncedSearch, filtersKey);
4546
4562
  }, [page, limit, debouncedSearch, filtersKey, fetchTableData]);
@@ -4618,6 +4634,42 @@ function TableBlockClient({
4618
4634
  }
4619
4635
  return [];
4620
4636
  }, [schemaColsString, executeAction, mergeRelationOptions]);
4637
+ const fetchRelationFilterOptions = useCallback10(async (colName) => {
4638
+ var _a;
4639
+ let parsedCols = [];
4640
+ try {
4641
+ parsedCols = JSON.parse(schemaColsString);
4642
+ } catch (e) {
4643
+ }
4644
+ const col = parsedCols.find((c) => {
4645
+ var _a2;
4646
+ return c.name === colName && ((_a2 = c.type) == null ? void 0 : _a2.toUpperCase()) === "RELATION";
4647
+ });
4648
+ if (!col || !((_a = col.config) == null ? void 0 : _a.referencedTableId) || !executeAction) return;
4649
+ const currentTableId = (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id);
4650
+ setLoadingFilterCol(colName);
4651
+ try {
4652
+ const res = await executeAction("FETCH_RELATION", {
4653
+ referencedTableId: col.config.referencedTableId,
4654
+ currentTableId,
4655
+ columnName: colName,
4656
+ isFilter: true,
4657
+ displayField: col.config.displayField,
4658
+ take: 1e3
4659
+ });
4660
+ const list = Array.isArray(res) ? res : (res == null ? void 0 : res.data) && Array.isArray(res.data) ? res.data : [];
4661
+ setRelationFilterOptions((prev) => __spreadProps(__spreadValues({}, prev), {
4662
+ [colName]: list
4663
+ }));
4664
+ if (list.length > 0) {
4665
+ mergeRelationOptions(colName, list);
4666
+ }
4667
+ } catch (err) {
4668
+ console.warn(`[TableBlock] Failed to fetch distinct relation filter options for ${colName}:`, err);
4669
+ } finally {
4670
+ setLoadingFilterCol(null);
4671
+ }
4672
+ }, [schemaColsString, schema == null ? void 0 : schema.tableMetaId, schema == null ? void 0 : schema.tableId, schema == null ? void 0 : schema.id, executeAction, mergeRelationOptions]);
4621
4673
  const requestedRelationIdsRef = useRef8({});
4622
4674
  useEffect10(() => {
4623
4675
  if (!executeAction || !data || data.length === 0) return;
@@ -4920,39 +4972,38 @@ function TableBlockClient({
4920
4972
  setData((prev) => [newRow, ...prev]);
4921
4973
  setDirty((prev) => __spreadProps(__spreadValues({}, prev), { added: [newRow, ...prev.added] }));
4922
4974
  };
4923
- const handleCellChange = (rowId, colName, value) => {
4975
+ const handleCellChange = useCallback10((rowId, colName, value) => {
4924
4976
  setData((prev) => prev.map(
4925
4977
  (row) => row.id === rowId ? __spreadProps(__spreadValues({}, row), { data: __spreadProps(__spreadValues({}, row.data), { [colName]: value }) }) : row
4926
4978
  ));
4927
- const isAdded = dirty.added.some((r) => r.id === rowId);
4928
- if (isAdded) {
4929
- setDirty((prev) => __spreadProps(__spreadValues({}, prev), {
4930
- added: prev.added.map(
4931
- (r) => r.id === rowId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value }) }) : r
4932
- )
4933
- }));
4934
- } else {
4935
- const origData = baseRecordsMap.get(rowId) || {};
4936
- const origValue = origData[colName];
4937
- setDirty((prev) => {
4938
- const currentMods = __spreadValues({}, prev.modified[rowId] || {});
4939
- if (isEqualValues(value, origValue)) {
4940
- delete currentMods[colName];
4941
- } else {
4942
- currentMods[colName] = value;
4943
- }
4944
- const nextModified = __spreadValues({}, prev.modified);
4945
- if (Object.keys(currentMods).length === 0) {
4946
- delete nextModified[rowId];
4947
- } else {
4948
- nextModified[rowId] = currentMods;
4949
- }
4979
+ setDirty((prev) => {
4980
+ const isAdded = prev.added.some((r) => r.id === rowId);
4981
+ if (isAdded) {
4950
4982
  return __spreadProps(__spreadValues({}, prev), {
4951
- modified: nextModified
4983
+ added: prev.added.map(
4984
+ (r) => r.id === rowId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value }) }) : r
4985
+ )
4952
4986
  });
4987
+ }
4988
+ const origData = baseRecordsMap.get(rowId) || {};
4989
+ const origValue = origData[colName];
4990
+ const currentMods = __spreadValues({}, prev.modified[rowId] || {});
4991
+ if (isEqualValues(value, origValue)) {
4992
+ delete currentMods[colName];
4993
+ } else {
4994
+ currentMods[colName] = value;
4995
+ }
4996
+ const nextModified = __spreadValues({}, prev.modified);
4997
+ if (Object.keys(currentMods).length === 0) {
4998
+ delete nextModified[rowId];
4999
+ } else {
5000
+ nextModified[rowId] = currentMods;
5001
+ }
5002
+ return __spreadProps(__spreadValues({}, prev), {
5003
+ modified: nextModified
4953
5004
  });
4954
- }
4955
- };
5005
+ });
5006
+ }, [baseRecordsMap]);
4956
5007
  const triggerSaveSuccess = useCallback10(() => {
4957
5008
  setIsSaveSuccess(true);
4958
5009
  setTimeout(() => {
@@ -4983,22 +5034,28 @@ function TableBlockClient({
4983
5034
  });
4984
5035
  }
4985
5036
  }, []);
4986
- const toggleSelectAll = (checked) => {
4987
- const nextSelected = checked ? new Set(filteredData.map((r) => r.id)) : /* @__PURE__ */ new Set();
4988
- setSelectedIds(nextSelected);
4989
- if (emitEvent) {
4990
- emitEvent("SELECTION_CHANGE", { selectedIds: Array.from(nextSelected), componentId });
4991
- }
4992
- };
4993
- const toggleSelectRow = (rowId, checked) => {
4994
- const newSelected = new Set(selectedIds);
4995
- if (checked) newSelected.add(rowId);
4996
- else newSelected.delete(rowId);
4997
- setSelectedIds(newSelected);
4998
- if (emitEvent) {
4999
- emitEvent("SELECTION_CHANGE", { selectedIds: Array.from(newSelected), componentId });
5000
- }
5001
- };
5037
+ const toggleSelectAll = useCallback10((checked) => {
5038
+ setSelectedIds(checked ? new Set(filteredData.map((r) => r.id)) : /* @__PURE__ */ new Set());
5039
+ }, [filteredData]);
5040
+ const toggleSelectRow = useCallback10((rowId, checked) => {
5041
+ setSelectedIds((prev) => {
5042
+ const next = new Set(prev);
5043
+ if (checked) next.add(rowId);
5044
+ else next.delete(rowId);
5045
+ return next;
5046
+ });
5047
+ }, []);
5048
+ const handleToggleExpand = useCallback10((rowId) => {
5049
+ setExpandedRowIds((prev) => {
5050
+ const next = new Set(prev);
5051
+ if (next.has(rowId)) next.delete(rowId);
5052
+ else next.add(rowId);
5053
+ return next;
5054
+ });
5055
+ }, []);
5056
+ const handleMarkForDeletion = useCallback10((fileId) => {
5057
+ setPendingDeletions((prev) => [...prev, fileId]);
5058
+ }, []);
5002
5059
  const handleDeleteSelected = () => {
5003
5060
  const idsToDelete = Array.from(selectedIds);
5004
5061
  if (idsToDelete.length === 0) return;
@@ -5077,9 +5134,6 @@ function TableBlockClient({
5077
5134
  setData(serverRecords.map((r) => __spreadProps(__spreadValues({}, r), { data: __spreadValues({}, r.data) })));
5078
5135
  setSelectedIds(/* @__PURE__ */ new Set());
5079
5136
  setPendingDeletions([]);
5080
- if (emitEvent) {
5081
- emitEvent("SELECTION_CHANGE", { selectedIds: [], componentId });
5082
- }
5083
5137
  };
5084
5138
  const handleSave = async () => {
5085
5139
  var _a, _b;
@@ -5247,26 +5301,13 @@ function TableBlockClient({
5247
5301
  }).filter(Boolean);
5248
5302
  }, [relationOptions]);
5249
5303
  const getUniqueRelationOptions = useCallback10((col) => {
5304
+ if (relationFilterOptions[col.name]) {
5305
+ return relationFilterOptions[col.name];
5306
+ }
5250
5307
  const seenIds = /* @__PURE__ */ new Set();
5251
5308
  const seenLabels = /* @__PURE__ */ new Set();
5252
5309
  const results = [];
5253
5310
  const options = relationOptions[col.name] || [];
5254
- options.forEach((opt) => {
5255
- const id = String(opt.id || opt._id || "");
5256
- if (!id) return;
5257
- let label = resolveRelationLabel(opt, col);
5258
- if (!label) {
5259
- const fallback = opt.label || opt.name || opt.title;
5260
- if (fallback && typeof fallback === "string" && !isRawObjectId(fallback)) {
5261
- label = fallback.trim();
5262
- }
5263
- }
5264
- if (label && label.trim() && !isRawObjectId(label) && !seenLabels.has(label.trim())) {
5265
- seenIds.add(id);
5266
- seenLabels.add(label.trim());
5267
- results.push({ id, label: label.trim() });
5268
- }
5269
- });
5270
5311
  data.forEach((row) => {
5271
5312
  if (!(row == null ? void 0 : row.data)) return;
5272
5313
  const ids = normalizeRelationValue(row.data[col.name]);
@@ -5289,8 +5330,7 @@ function TableBlockClient({
5289
5330
  });
5290
5331
  });
5291
5332
  return results.sort((a, b) => a.label.localeCompare(b.label));
5292
- }, [data, relationOptions]);
5293
- const filteredData = data;
5333
+ }, [data, relationOptions, relationFilterOptions]);
5294
5334
  const errorMessage = error ? typeof error === "string" ? error : error.message || String(error) : null;
5295
5335
  const columnTotals = useMemo6(() => {
5296
5336
  const totals = {};
@@ -5381,6 +5421,13 @@ function TableBlockClient({
5381
5421
  link.click();
5382
5422
  document.body.removeChild(link);
5383
5423
  URL.revokeObjectURL(url);
5424
+ if (result == null ? void 0 : result.isTruncated) {
5425
+ addToast({
5426
+ message: "\u05D9\u05D5\u05E6\u05D0\u05D5 10,000 \u05D4\u05E9\u05D5\u05E8\u05D5\u05EA \u05D4\u05E8\u05D0\u05E9\u05D5\u05E0\u05D5\u05EA. \u05DE\u05D5\u05DE\u05DC\u05E5 \u05DC\u05E1\u05E0\u05DF \u05D0\u05EA \u05D4\u05D8\u05D1\u05DC\u05D4 \u05DB\u05D3\u05D9 \u05DC\u05E6\u05DE\u05E6\u05DD \u05D0\u05EA \u05D4\u05DB\u05DE\u05D5\u05EA",
5427
+ type: "warning",
5428
+ duration: 5e3
5429
+ });
5430
+ }
5384
5431
  } catch (err) {
5385
5432
  console.error("[TableBlock] Export to CSV failed:", err);
5386
5433
  showError((err == null ? void 0 : err.message) || String(err), "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D9\u05D9\u05E6\u05D5\u05D0 \u05D4\u05E0\u05EA\u05D5\u05E0\u05D9\u05DD");
@@ -5403,7 +5450,7 @@ function TableBlockClient({
5403
5450
  /* @__PURE__ */ jsxs11("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 shrink-0", children: [
5404
5451
  /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center gap-2.5", children: [
5405
5452
  ((schema == null ? void 0 : schema.title) || (schema == null ? void 0 : schema.name)) && /* @__PURE__ */ jsx11("div", { className: "font-bold text-slate-800 text-sm sm:text-base border-l border-slate-300 pl-3 ml-1", children: /* @__PURE__ */ jsx11("span", { children: schema.title || schema.name }) }),
5406
- /* @__PURE__ */ jsx11(
5453
+ !effectiveReadOnly && effectivePermissions.canCreate && /* @__PURE__ */ jsx11(
5407
5454
  "button",
5408
5455
  {
5409
5456
  onClick: handleAddRow,
@@ -5413,7 +5460,7 @@ function TableBlockClient({
5413
5460
  children: /* @__PURE__ */ jsx11("svg", { className: "w-4.5 h-4.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M12 4v16m8-8H4" }) })
5414
5461
  }
5415
5462
  ),
5416
- /* @__PURE__ */ jsx11(
5463
+ !effectiveReadOnly && effectivePermissions.canCreate && /* @__PURE__ */ jsx11(
5417
5464
  "button",
5418
5465
  {
5419
5466
  onClick: handleDuplicateRow,
@@ -5423,7 +5470,7 @@ function TableBlockClient({
5423
5470
  children: /* @__PURE__ */ jsx11(Copy2, { className: "w-4 h-4 text-blue-600 shrink-0" })
5424
5471
  }
5425
5472
  ),
5426
- /* @__PURE__ */ jsxs11(
5473
+ !effectiveReadOnly && effectivePermissions.canDelete && /* @__PURE__ */ jsxs11(
5427
5474
  "button",
5428
5475
  {
5429
5476
  onClick: handleDeleteSelected,
@@ -5449,7 +5496,7 @@ function TableBlockClient({
5449
5496
  ]
5450
5497
  }
5451
5498
  ),
5452
- /* @__PURE__ */ jsxs11("div", { className: "relative", ref: dropdownRef, children: [
5499
+ !effectiveReadOnly && effectivePermissions.canUpdate && /* @__PURE__ */ jsxs11("div", { className: "relative", ref: dropdownRef, children: [
5453
5500
  /* @__PURE__ */ jsxs11(
5454
5501
  "button",
5455
5502
  {
@@ -5587,7 +5634,7 @@ function TableBlockClient({
5587
5634
  ] }) : isSaveSuccess ? /* @__PURE__ */ jsxs11("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: [
5588
5635
  /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4 text-emerald-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2.5", d: "M5 13l4 4L19 7" }) }),
5589
5636
  "\u05E2\u05D5\u05D3\u05DB\u05DF \u05D1\u05D4\u05E6\u05DC\u05D7\u05D4"
5590
- ] }) : hasChanges ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 animate-in fade-in", children: [
5637
+ ] }) : !effectiveReadOnly && effectivePermissions.canUpdate && hasChanges ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 animate-in fade-in", children: [
5591
5638
  /* @__PURE__ */ jsx11("span", { className: "text-xs font-semibold text-amber-700", children: "\u05E9\u05D9\u05E0\u05D5\u05D9\u05D9\u05DD \u05E9\u05DC\u05D0 \u05E0\u05E9\u05DE\u05E8\u05D5" }),
5592
5639
  /* @__PURE__ */ jsx11(
5593
5640
  "button",
@@ -5602,7 +5649,7 @@ function TableBlockClient({
5602
5649
  }
5603
5650
  )
5604
5651
  ] }) : null,
5605
- /* @__PURE__ */ jsx11(
5652
+ !effectiveReadOnly && effectivePermissions.canUpdate && /* @__PURE__ */ jsx11(
5606
5653
  "button",
5607
5654
  {
5608
5655
  onClick: handleSave,
@@ -5640,9 +5687,10 @@ function TableBlockClient({
5640
5687
  filteredColumns.map((col) => {
5641
5688
  var _a, _b;
5642
5689
  const isSearching = activeSearchCol === col.name;
5643
- const dragProps = isSearching ? {} : getHeaderDragProps(col.name);
5644
- const isDragging = !isSearching && draggedColName === col.name;
5645
- const isDragOver = !isSearching && dragOverColName === col.name;
5690
+ const canReorderColumns = !effectiveReadOnly && effectivePermissions.canUpdate && !isSearching;
5691
+ const dragProps = canReorderColumns ? getHeaderDragProps(col.name) : {};
5692
+ const isDragging = canReorderColumns && draggedColName === col.name;
5693
+ const isDragOver = canReorderColumns && dragOverColName === col.name;
5646
5694
  const colMinWidth = calculateMinColumnWidth(col.name, false, col.type);
5647
5695
  const currentWidth = Math.max(colMinWidth, columnWidths[col.name] || 200);
5648
5696
  const isRelation = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) === "RELATION";
@@ -5662,7 +5710,7 @@ function TableBlockClient({
5662
5710
  style: { width: currentWidth, minWidth: currentWidth, maxWidth: currentWidth },
5663
5711
  title: isSearching ? void 0 : col.name
5664
5712
  }, dragProps), {
5665
- className: `group relative border-b border-l border-slate-200 px-2 py-1 font-semibold text-slate-700 select-none transition-colors h-12 min-h-[48px] align-middle bg-white ${isSearching ? "cursor-default bg-blue-50/40" : "cursor-grab active:cursor-grabbing hover:bg-slate-100"} ${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" : ""}`,
5713
+ className: `group relative border-b border-l border-slate-200 px-2 py-1 font-semibold text-slate-700 select-none transition-colors h-12 min-h-[48px] align-middle bg-white ${isSearching ? "cursor-default bg-blue-50/40" : canReorderColumns ? "cursor-grab active:cursor-grabbing hover:bg-slate-100" : "cursor-default"} ${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" : ""}`,
5666
5714
  children: [
5667
5715
  /* @__PURE__ */ jsx11(
5668
5716
  "div",
@@ -5743,9 +5791,7 @@ function TableBlockClient({
5743
5791
  setRelationTriggerRect(btn.getBoundingClientRect());
5744
5792
  }
5745
5793
  setActiveRelationCol(col.name);
5746
- if (!relationOptions[col.name] || relationOptions[col.name].length < 100) {
5747
- refetchRelation(col.name, { take: 1e3 });
5748
- }
5794
+ fetchRelationFilterOptions(col.name);
5749
5795
  }
5750
5796
  } else {
5751
5797
  setActiveSearchCol(col.name);
@@ -5778,106 +5824,39 @@ function TableBlockClient({
5778
5824
  var _a;
5779
5825
  return ((_a = c.type) == null ? void 0 : _a.toUpperCase()) === "SUB_TABLE";
5780
5826
  });
5781
- return /* @__PURE__ */ jsxs11(React11.Fragment, { children: [
5782
- /* @__PURE__ */ jsxs11("tr", { className: "hover:bg-blue-50/30 transition-colors group", children: [
5783
- /* @__PURE__ */ jsx11("td", { className: "border-b border-l border-slate-200 p-1.5 text-center bg-white group-hover:bg-transparent", style: { width: 72, minWidth: 72, maxWidth: 72 }, children: /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-center gap-1.5", children: [
5784
- /* @__PURE__ */ jsx11(
5785
- "input",
5786
- {
5787
- type: "checkbox",
5788
- checked: selectedIds.has(row.id),
5789
- onChange: (e) => toggleSelectRow(row.id, e.target.checked),
5790
- disabled: isLoading || isUpdatingActive,
5791
- className: "w-4 h-4 rounded border-slate-300 text-brand"
5792
- }
5793
- ),
5794
- hasSubTableCols && /* @__PURE__ */ jsx11(
5795
- "button",
5796
- {
5797
- type: "button",
5798
- onClick: () => setExpandedRowIds((prev) => {
5799
- const next = new Set(prev);
5800
- if (next.has(row.id)) next.delete(row.id);
5801
- else next.add(row.id);
5802
- return next;
5803
- }),
5804
- className: "w-5 h-5 rounded-full bg-slate-500 hover:bg-slate-600 text-white flex items-center justify-center transition-colors cursor-pointer shrink-0 shadow-sm",
5805
- title: isExpanded ? "\u05E1\u05D2\u05D5\u05E8 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4" : "\u05E4\u05EA\u05D7 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4",
5806
- children: /* @__PURE__ */ jsx11("svg", { className: `w-3 h-3 transition-transform duration-150 ${isExpanded ? "rotate-90" : "rotate-180"}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "3", d: "M9 5l7 7-7 7" }) })
5807
- }
5808
- )
5809
- ] }) }),
5810
- filteredColumns.map((col) => {
5811
- var _a;
5812
- const colType = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
5813
- const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
5814
- const colMinWidth = calculateMinColumnWidth(col.name, false, col.type);
5815
- const currentWidth = Math.max(colMinWidth, columnWidths[col.name] || 200);
5816
- let value = (row == null ? void 0 : row.data) ? row.data[col.name] : void 0;
5817
- if (value === void 0 && col.defaultValue !== void 0) {
5818
- value = colType === "CURRENCY" || colType === "NUMBER" ? parseFloat(col.defaultValue) : col.defaultValue;
5819
- if (colType === "CURRENCY" || colType === "NUMBER") {
5820
- if (isNaN(value)) value = null;
5821
- }
5822
- }
5823
- if (value === void 0) value = null;
5824
- return /* @__PURE__ */ jsx11(
5825
- "td",
5826
- {
5827
- className: `border-b border-l border-slate-200 p-0 relative ${colType === "RELATION" ? "overflow-visible" : "overflow-hidden"}`,
5828
- style: { width: currentWidth, minWidth: currentWidth, maxWidth: currentWidth },
5829
- children: /* @__PURE__ */ jsx11(
5830
- InputComponent,
5831
- {
5832
- value,
5833
- column: col,
5834
- rowId: row.id,
5835
- onChange: (v) => handleCellChange(row.id, col.name, v),
5836
- context: {
5837
- 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",
5838
- tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
5839
- onMarkForDeletion: (fileId) => setPendingDeletions((prev) => [...prev, fileId]),
5840
- relationOptions,
5841
- relationDictionaryRef,
5842
- refetchRelation,
5843
- executeAction,
5844
- emitEvent,
5845
- pageState,
5846
- componentId,
5847
- orgId,
5848
- updateQuietly: handleQuietCellChange,
5849
- triggerSaveSuccess,
5850
- rowData: row.data
5851
- }
5852
- }
5853
- )
5854
- },
5855
- col.name
5856
- );
5857
- }),
5858
- /* @__PURE__ */ jsx11("td", { className: "border-b border-slate-200 bg-white pointer-events-none" })
5859
- ] }),
5860
- isExpanded && /* @__PURE__ */ jsx11(
5861
- ExpandedSubTables,
5862
- {
5863
- rowId: row.id,
5864
- rowData: row.data,
5865
- subTableColumns,
5866
- filteredColumnsLength: filteredColumns.length,
5867
- handleCellChange,
5868
- context: {
5869
- executeAction,
5870
- orgId,
5871
- parentTableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
5872
- tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
5873
- rowData: row.data,
5874
- updateQuietly: handleQuietCellChange,
5875
- triggerSaveSuccess,
5876
- onUpdateColumnConfig: handleUpdateColumnConfig
5877
- }
5878
- }
5879
- )
5880
- ] }, row.id);
5827
+ return /* @__PURE__ */ jsx11(
5828
+ TableRow,
5829
+ {
5830
+ row,
5831
+ isSelected: selectedIds.has(row.id),
5832
+ isExpanded,
5833
+ filteredColumns,
5834
+ columnWidths,
5835
+ hasSubTableCols,
5836
+ subTableColumns,
5837
+ isLoading: isLoading || isFetching,
5838
+ isUpdatingActive,
5839
+ onToggleSelect: toggleSelectRow,
5840
+ onToggleExpand: handleToggleExpand,
5841
+ handleCellChange,
5842
+ handleQuietCellChange,
5843
+ handleUpdateColumnConfig,
5844
+ onMarkForDeletion: handleMarkForDeletion,
5845
+ triggerSaveSuccess,
5846
+ schema,
5847
+ orgId,
5848
+ componentId,
5849
+ pageState,
5850
+ relationOptions,
5851
+ relationDictionaryRef,
5852
+ refetchRelation,
5853
+ executeAction,
5854
+ emitEvent,
5855
+ readOnly: effectiveReadOnly || !effectivePermissions.canUpdate,
5856
+ permissions: effectivePermissions
5857
+ },
5858
+ row.id
5859
+ );
5881
5860
  }) }),
5882
5861
  showSummary && filteredData.length > 0 && /* @__PURE__ */ jsx11("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__ */ jsxs11("tr", { children: [
5883
5862
  /* @__PURE__ */ jsx11("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' }),
@@ -5973,7 +5952,8 @@ function TableBlockClient({
5973
5952
  filterValue: currentSelected,
5974
5953
  onSelectionChange: (newSelected) => {
5975
5954
  handleApplyColumnFilter(activeRelationCol, newSelected);
5976
- }
5955
+ },
5956
+ isLoading: loadingFilterCol === activeRelationCol
5977
5957
  }
5978
5958
  );
5979
5959
  })()
@@ -6024,12 +6004,143 @@ function ExpandedSubTables({
6024
6004
  column: activeCol,
6025
6005
  value: (rowData == null ? void 0 : rowData[activeCol.name]) || [],
6026
6006
  onChange: (newVal) => handleCellChange(rowId, activeCol.name, newVal),
6027
- context
6007
+ context,
6008
+ readOnly: context == null ? void 0 : context.readOnly
6028
6009
  },
6029
6010
  activeCol.name
6030
6011
  ) })
6031
6012
  ] }) });
6032
6013
  }
6014
+ var TableRow = React11.memo(function TableRow2({
6015
+ row,
6016
+ isSelected,
6017
+ isExpanded,
6018
+ filteredColumns,
6019
+ columnWidths,
6020
+ hasSubTableCols,
6021
+ subTableColumns,
6022
+ isLoading,
6023
+ isUpdatingActive,
6024
+ onToggleSelect,
6025
+ onToggleExpand,
6026
+ handleCellChange,
6027
+ handleQuietCellChange,
6028
+ handleUpdateColumnConfig,
6029
+ onMarkForDeletion,
6030
+ triggerSaveSuccess,
6031
+ schema,
6032
+ orgId,
6033
+ componentId,
6034
+ pageState,
6035
+ relationOptions,
6036
+ relationDictionaryRef,
6037
+ refetchRelation,
6038
+ executeAction,
6039
+ emitEvent,
6040
+ readOnly,
6041
+ permissions
6042
+ }) {
6043
+ return /* @__PURE__ */ jsxs11(React11.Fragment, { children: [
6044
+ /* @__PURE__ */ jsxs11("tr", { className: `hover:bg-blue-50/30 transition-colors group ${isSelected ? "bg-blue-50/40" : ""}`, children: [
6045
+ /* @__PURE__ */ jsx11("td", { className: "border-b border-l border-slate-200 p-1.5 text-center bg-white group-hover:bg-transparent", style: { width: 72, minWidth: 72, maxWidth: 72 }, children: /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-center gap-1.5", children: [
6046
+ /* @__PURE__ */ jsx11(
6047
+ "input",
6048
+ {
6049
+ type: "checkbox",
6050
+ checked: isSelected,
6051
+ onChange: (e) => onToggleSelect(row.id, e.target.checked),
6052
+ disabled: isLoading || isUpdatingActive,
6053
+ className: "w-4 h-4 rounded border-slate-300 text-brand"
6054
+ }
6055
+ ),
6056
+ hasSubTableCols && /* @__PURE__ */ jsx11(
6057
+ "button",
6058
+ {
6059
+ type: "button",
6060
+ onClick: () => onToggleExpand(row.id),
6061
+ className: "w-5 h-5 rounded-full bg-slate-500 hover:bg-slate-600 text-white flex items-center justify-center transition-colors cursor-pointer shrink-0 shadow-sm",
6062
+ title: isExpanded ? "\u05E1\u05D2\u05D5\u05E8 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4" : "\u05E4\u05EA\u05D7 \u05EA\u05EA-\u05D8\u05D1\u05DC\u05D4",
6063
+ children: /* @__PURE__ */ jsx11("svg", { className: `w-3 h-3 transition-transform duration-150 ${isExpanded ? "rotate-90" : "rotate-180"}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "3", d: "M9 5l7 7-7 7" }) })
6064
+ }
6065
+ )
6066
+ ] }) }),
6067
+ filteredColumns.map((col) => {
6068
+ var _a;
6069
+ const colType = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
6070
+ const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
6071
+ const colMinWidth = calculateMinColumnWidth(col.name, false, col.type);
6072
+ const currentWidth = Math.max(colMinWidth, columnWidths[col.name] || 200);
6073
+ let value = (row == null ? void 0 : row.data) ? row.data[col.name] : void 0;
6074
+ if (value === void 0 && col.defaultValue !== void 0) {
6075
+ value = colType === "CURRENCY" || colType === "NUMBER" ? parseFloat(col.defaultValue) : col.defaultValue;
6076
+ if (colType === "CURRENCY" || colType === "NUMBER") {
6077
+ if (isNaN(value)) value = null;
6078
+ }
6079
+ }
6080
+ if (value === void 0) value = null;
6081
+ return /* @__PURE__ */ jsx11(
6082
+ "td",
6083
+ {
6084
+ className: `border-b border-l border-slate-200 p-0 relative ${colType === "RELATION" ? "overflow-visible" : "overflow-hidden"}`,
6085
+ style: { width: currentWidth, minWidth: currentWidth, maxWidth: currentWidth },
6086
+ children: /* @__PURE__ */ jsx11(
6087
+ InputComponent,
6088
+ {
6089
+ value,
6090
+ column: col,
6091
+ rowId: row.id,
6092
+ onChange: (v) => handleCellChange(row.id, col.name, v),
6093
+ readOnly,
6094
+ context: {
6095
+ 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",
6096
+ tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
6097
+ onMarkForDeletion,
6098
+ relationOptions,
6099
+ relationDictionaryRef,
6100
+ refetchRelation,
6101
+ executeAction,
6102
+ emitEvent,
6103
+ pageState,
6104
+ componentId,
6105
+ orgId,
6106
+ updateQuietly: handleQuietCellChange,
6107
+ triggerSaveSuccess,
6108
+ rowData: row.data,
6109
+ readOnly,
6110
+ permissions
6111
+ }
6112
+ }
6113
+ )
6114
+ },
6115
+ col.name
6116
+ );
6117
+ }),
6118
+ /* @__PURE__ */ jsx11("td", { className: "border-b border-slate-200 bg-white pointer-events-none" })
6119
+ ] }),
6120
+ isExpanded && /* @__PURE__ */ jsx11(
6121
+ ExpandedSubTables,
6122
+ {
6123
+ rowId: row.id,
6124
+ rowData: row.data,
6125
+ subTableColumns,
6126
+ filteredColumnsLength: filteredColumns.length,
6127
+ handleCellChange,
6128
+ context: {
6129
+ executeAction,
6130
+ orgId,
6131
+ parentTableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
6132
+ tableId: (schema == null ? void 0 : schema.tableMetaId) || (schema == null ? void 0 : schema.tableId) || (schema == null ? void 0 : schema.id),
6133
+ rowData: row.data,
6134
+ updateQuietly: handleQuietCellChange,
6135
+ triggerSaveSuccess,
6136
+ onUpdateColumnConfig: handleUpdateColumnConfig,
6137
+ readOnly,
6138
+ permissions
6139
+ }
6140
+ }
6141
+ )
6142
+ ] });
6143
+ });
6033
6144
 
6034
6145
  // src/components/TableBlock/TableBlock.tsx
6035
6146
  import { jsx as jsx12 } from "react/jsx-runtime";
@@ -6065,6 +6176,7 @@ export {
6065
6176
  formatTimeInput,
6066
6177
  formatTimeIso,
6067
6178
  getDeterministicPastelBadge,
6179
+ getRolePermissions,
6068
6180
  isTextRepresentable,
6069
6181
  measureTextWidth,
6070
6182
  normalizeRelationValue,