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

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.js CHANGED
@@ -58,16 +58,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
58
58
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
59
59
 
60
60
  // src/index.ts
61
- var index_exports = {};
62
- __export(index_exports, {
61
+ var src_exports = {};
62
+ __export(src_exports, {
63
63
  TableBlock: () => TableBlock,
64
64
  ToastContext: () => ToastContext,
65
65
  ToastProvider: () => ToastProvider,
66
66
  UNIVERSE_COMPONENTS: () => UNIVERSE_COMPONENTS,
67
67
  cn: () => cn,
68
+ formatPhoneDisplay: () => formatPhoneDisplay,
69
+ openWhatsAppChat: () => openWhatsAppChat,
68
70
  useToast: () => useToast
69
71
  });
70
- module.exports = __toCommonJS(index_exports);
72
+ module.exports = __toCommonJS(src_exports);
71
73
 
72
74
  // src/utils/cn.ts
73
75
  var import_clsx = require("clsx");
@@ -76,6 +78,36 @@ function cn(...inputs) {
76
78
  return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
77
79
  }
78
80
 
81
+ // src/utils/whatsapp.ts
82
+ function openWhatsAppChat(phone, defaultCountryCode = "972") {
83
+ if (!phone || typeof window === "undefined") return;
84
+ let cleaned = phone.replace(/\D/g, "");
85
+ if (cleaned.startsWith("0")) {
86
+ cleaned = defaultCountryCode + cleaned.substring(1);
87
+ }
88
+ const appUrl = `whatsapp://send?phone=${cleaned}`;
89
+ const webUrl = `https://web.whatsapp.com/send?phone=${cleaned}`;
90
+ const start = Date.now();
91
+ window.location.href = appUrl;
92
+ setTimeout(() => {
93
+ if (typeof document !== "undefined" && document.hasFocus() && Date.now() - start < 2e3) {
94
+ window.open(webUrl, "_blank", "noopener,noreferrer");
95
+ }
96
+ }, 1200);
97
+ }
98
+ function formatPhoneDisplay(phone) {
99
+ if (!phone) return "";
100
+ const digits = phone.replace(/\D/g, "");
101
+ if (digits.length === 10 && digits.startsWith("05")) {
102
+ return `${digits.slice(0, 3)}-${digits.slice(3, 6)}-${digits.slice(6)}`;
103
+ }
104
+ if (digits.length === 12 && digits.startsWith("9725")) {
105
+ const local = "0" + digits.slice(3);
106
+ return `${local.slice(0, 3)}-${local.slice(3, 6)}-${local.slice(6)}`;
107
+ }
108
+ return phone;
109
+ }
110
+
79
111
  // src/components/TableBlock/TableBlockClient.tsx
80
112
  var import_react7 = __toESM(require("react"));
81
113
 
@@ -927,16 +959,35 @@ var FORM_INPUTS = {
927
959
  }
928
960
  }
929
961
  ),
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
- ),
962
+ PHONE: ({ value, onChange, column }) => {
963
+ var _a;
964
+ const enableWhatsApp = (_a = column == null ? void 0 : column.config) == null ? void 0 : _a.enableWhatsApp;
965
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center w-full h-full px-2 gap-1.5", children: [
966
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
967
+ "input",
968
+ {
969
+ type: "tel",
970
+ className: "w-full h-8 bg-transparent focus:bg-white focus:outline-none focus:ring-1 focus:ring-blue-400 text-sm flex-1",
971
+ value: value !== void 0 && value !== null ? String(value) : "",
972
+ onChange: (e) => onChange(e.target.value),
973
+ dir: "ltr"
974
+ }
975
+ ),
976
+ enableWhatsApp && value && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
977
+ "button",
978
+ {
979
+ type: "button",
980
+ className: "text-emerald-500 cursor-pointer hover:opacity-80 transition-opacity shrink-0 p-1 rounded hover:bg-emerald-50",
981
+ title: "\u05E4\u05EA\u05D9\u05D7\u05EA \u05D5\u05D5\u05D0\u05D8\u05E1\u05D0\u05E4",
982
+ onClick: (e) => {
983
+ e.stopPropagation();
984
+ openWhatsAppChat(String(value));
985
+ },
986
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { className: "w-4 h-4", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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" }) })
987
+ }
988
+ )
989
+ ] });
990
+ },
940
991
  GOOGLE_CONTACT: ({ value, column, rowId, onChange, context }) => {
941
992
  const [isSyncing, setIsSyncing] = import_react5.default.useState(false);
942
993
  const { addToast } = useToast();
@@ -1120,31 +1171,33 @@ var FORM_INPUTS = {
1120
1171
  }
1121
1172
  };
1122
1173
  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" }),
1174
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-1.5 h-8 px-2 group whitespace-nowrap", children: [
1175
+ /* @__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-xs whitespace-nowrap", children: "\u05D0\u05D9\u05E9 \u05E7\u05E9\u05E8 \u05D2\u05D5\u05D2\u05DC" }),
1125
1176
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1126
1177
  "button",
1127
1178
  {
1179
+ type: "button",
1128
1180
  onClick: handleUnlink,
1129
- className: "text-slate-400 hover:text-rose-500 opacity-0 group-hover:opacity-100 transition-opacity p-1",
1181
+ className: "text-slate-400 hover:text-rose-500 opacity-0 group-hover:opacity-100 transition-opacity p-1 shrink-0",
1130
1182
  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" }) })
1183
+ children: /* @__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: "M6 18L18 6M6 6l12 12" }) })
1132
1184
  }
1133
1185
  )
1134
1186
  ] });
1135
1187
  }
1136
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex items-center h-8 px-2", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1188
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex items-center h-8 px-2 whitespace-nowrap", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1137
1189
  "button",
1138
1190
  {
1191
+ type: "button",
1139
1192
  onClick: handleSync,
1140
1193
  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",
1194
+ className: "inline-flex items-center gap-1.5 px-2.5 py-1 bg-blue-50 text-blue-600 hover:bg-blue-100 border border-blue-200 rounded-md text-xs font-medium transition-colors whitespace-nowrap shrink-0 disabled:opacity-50 cursor-pointer",
1142
1195
  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: [
1196
+ isSyncing ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { className: "w-3.5 h-3.5 animate-spin shrink-0", viewBox: "0 0 24 24", fill: "none", children: [
1144
1197
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1145
1198
  /* @__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"
1199
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { className: "w-3.5 h-3.5 shrink-0", 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" }) }),
1200
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "whitespace-nowrap", children: "\u05E1\u05E0\u05DB\u05E8\u05DF \u05DC\u05D2\u05D5\u05D2\u05DC" })
1148
1201
  ]
1149
1202
  }
1150
1203
  ) });
@@ -1159,17 +1212,48 @@ var FORM_INPUTS = {
1159
1212
  }
1160
1213
  ),
1161
1214
  SUB_TABLE: ({ value, column }) => {
1215
+ var _a, _b;
1162
1216
  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
1217
  const selected = value.filter((e) => e == null ? void 0 : e.isSelected);
1164
1218
  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
1219
  const displayFields = column.subTableDisplayFields || [];
1220
+ const isWhatsApp = ((_a = column.config) == null ? void 0 : _a.actionType) === "WHATSAPP";
1221
+ const actionPhoneCol = (_b = column.config) == null ? void 0 : _b.actionPhoneColumn;
1166
1222
  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;
1223
+ var _a2, _b2;
1168
1224
  let label = "";
1169
- if (entry._resolvedData && displayFields.length > 0) {
1170
- label = displayFields.map((f) => String(entry._resolvedData[f] || "")).filter(Boolean).join(" ");
1225
+ if (entry._resolvedData) {
1226
+ if (displayFields.length > 0) {
1227
+ label = displayFields.map((f) => String(entry._resolvedData[f] || "")).filter(Boolean).join(" ");
1228
+ } else {
1229
+ const firstField = Object.keys(entry._resolvedData).find((k) => k !== "_id");
1230
+ if (firstField) label = String(entry._resolvedData[firstField] || "");
1231
+ }
1232
+ }
1233
+ if (!label) label = ((_a2 = entry.recordId) == null ? void 0 : _a2.slice(-6)) || "...";
1234
+ if (isWhatsApp) {
1235
+ const rawPhone = actionPhoneCol ? (_b2 = entry._resolvedData) == null ? void 0 : _b2[actionPhoneCol] : null;
1236
+ const phone = rawPhone ? String(rawPhone).trim() : null;
1237
+ if (phone) {
1238
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1239
+ "button",
1240
+ {
1241
+ type: "button",
1242
+ onClick: (e) => {
1243
+ e.stopPropagation();
1244
+ openWhatsAppChat(phone);
1245
+ },
1246
+ className: "inline-flex items-center gap-1 px-1.5 py-0.5 bg-emerald-50 hover:bg-emerald-100 text-emerald-800 border border-emerald-300 hover:border-emerald-400 rounded-full text-xs font-medium max-w-[150px] truncate cursor-pointer transition-colors shadow-xs",
1247
+ title: `\u05E4\u05EA\u05D9\u05D7\u05EA \u05D5\u05D5\u05D0\u05D8\u05E1\u05D0\u05E4 (${phone})`,
1248
+ children: [
1249
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { className: "w-3 h-3 text-emerald-600 shrink-0", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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" }) }),
1250
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate", children: label })
1251
+ ]
1252
+ },
1253
+ entry.recordId || idx
1254
+ );
1255
+ }
1171
1256
  }
1172
- if (!label) label = ((_a = entry.recordId) == null ? void 0 : _a.slice(-6)) || "...";
1173
1257
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1174
1258
  "span",
1175
1259
  {
@@ -1185,12 +1269,241 @@ var FORM_INPUTS = {
1185
1269
 
1186
1270
  // src/components/TableBlock/fields/SubTableViewer.tsx
1187
1271
  var import_react6 = require("react");
1272
+
1273
+ // src/utils/phone.ts
1274
+ function sanitizePhoneNumber(phone) {
1275
+ if (!phone) return "";
1276
+ const trimmed = phone.trim();
1277
+ const hasLeadingPlus = trimmed.startsWith("+");
1278
+ const digitsOnly = trimmed.replace(/\D/g, "");
1279
+ return hasLeadingPlus ? `+${digitsOnly}` : digitsOnly;
1280
+ }
1281
+ function isValidPhoneNumber(phone) {
1282
+ if (!phone) return false;
1283
+ const cleaned = sanitizePhoneNumber(phone);
1284
+ return /^\+?[0-9]{9,15}$/.test(cleaned);
1285
+ }
1286
+
1287
+ // src/components/TableBlock/fields/SubTableViewer.tsx
1188
1288
  var import_jsx_runtime5 = require("react/jsx-runtime");
1189
- function SubTableViewer({ rowId, column, value, onChange, context }) {
1289
+ function validateCellValue(val, col) {
1190
1290
  var _a;
1291
+ const colType = ((_a = col.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
1292
+ const isEmpty = val === void 0 || val === null || typeof val === "string" && val.trim() === "";
1293
+ if (col.isRequired && isEmpty) {
1294
+ return {
1295
+ isValid: false,
1296
+ error: `\u05E9\u05D2\u05D9\u05D0\u05EA \u05D0\u05D9\u05DE\u05D5\u05EA: \u05E9\u05D3\u05D4 '${col.name}' \u05D4\u05D9\u05E0\u05D5 \u05E9\u05D3\u05D4 \u05D7\u05D5\u05D1\u05D4 \u05D5\u05DC\u05D0 \u05D9\u05DB\u05D5\u05DC \u05DC\u05D4\u05D9\u05E9\u05D0\u05E8 \u05E8\u05D9\u05E7.`
1297
+ };
1298
+ }
1299
+ if (isEmpty) {
1300
+ return { isValid: true };
1301
+ }
1302
+ if (colType === "PHONE") {
1303
+ const strVal = String(val).trim();
1304
+ if (!isValidPhoneNumber(strVal)) {
1305
+ return {
1306
+ isValid: false,
1307
+ error: `\u05DE\u05E1\u05E4\u05E8 \u05D4\u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05E9\u05D3\u05D4 '${col.name}' \u05D0\u05D9\u05E0\u05D5 \u05EA\u05E7\u05D9\u05DF. \u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05DE\u05E1\u05E4\u05E8 \u05D1\u05D0\u05D5\u05E8\u05DA 9-15 \u05E1\u05E4\u05E8\u05D5\u05EA (\u05DC\u05D3\u05D5\u05D2\u05DE\u05D4 0501234567 \u05D0\u05D5 +972501234567).`
1308
+ };
1309
+ }
1310
+ }
1311
+ if (colType === "NUMBER" || colType === "CURRENCY") {
1312
+ if (typeof val === "string" && isNaN(Number(val))) {
1313
+ return {
1314
+ isValid: false,
1315
+ error: `\u05D4\u05E2\u05E8\u05DA \u05D1\u05E9\u05D3\u05D4 '${col.name}' \u05D7\u05D9\u05D9\u05D1 \u05DC\u05D4\u05D9\u05D5\u05EA \u05DE\u05E1\u05E4\u05E8 \u05EA\u05E7\u05D9\u05DF.`
1316
+ };
1317
+ }
1318
+ }
1319
+ return { isValid: true };
1320
+ }
1321
+ function SubTableCell({
1322
+ record,
1323
+ column,
1324
+ targetSchema,
1325
+ referencedTableId,
1326
+ context,
1327
+ relationOptions,
1328
+ setRelationOptions,
1329
+ refetchRelation,
1330
+ onCommit
1331
+ }) {
1332
+ var _a, _b;
1333
+ const colType = ((_a = column.type) == null ? void 0 : _a.toUpperCase()) || "DEFAULT";
1334
+ const isContact = colType === "GOOGLE_CONTACT";
1335
+ const InputComponent = FORM_INPUTS[colType] || FORM_INPUTS.DEFAULT;
1336
+ const serverValue = (_b = record.data[column.name]) != null ? _b : colType === "BOOLEAN" ? false : "";
1337
+ const [draftValue, setDraftValue] = (0, import_react6.useState)(serverValue);
1338
+ const [isDirty, setIsDirty] = (0, import_react6.useState)(false);
1339
+ const [validationError, setValidationError] = (0, import_react6.useState)(null);
1340
+ const cellRef = (0, import_react6.useRef)(null);
1341
+ const draftRef = (0, import_react6.useRef)(draftValue);
1342
+ const isDirtyRef = (0, import_react6.useRef)(isDirty);
1343
+ const serverValueRef = (0, import_react6.useRef)(serverValue);
1344
+ const recordIdRef = (0, import_react6.useRef)(record.id);
1345
+ const columnRef = (0, import_react6.useRef)(column);
1346
+ const onCommitRef = (0, import_react6.useRef)(onCommit);
1347
+ const isCommittingRef = (0, import_react6.useRef)(false);
1348
+ const lastFailedValRef = (0, import_react6.useRef)(void 0);
1349
+ const { addToast } = useToast();
1350
+ draftRef.current = draftValue;
1351
+ isDirtyRef.current = isDirty;
1352
+ serverValueRef.current = serverValue;
1353
+ recordIdRef.current = record.id;
1354
+ columnRef.current = column;
1355
+ onCommitRef.current = onCommit;
1356
+ (0, import_react6.useEffect)(() => {
1357
+ if (!isDirtyRef.current) {
1358
+ setDraftValue(serverValue);
1359
+ draftRef.current = serverValue;
1360
+ setValidationError(null);
1361
+ lastFailedValRef.current = void 0;
1362
+ }
1363
+ }, [serverValue]);
1364
+ const commit = (0, import_react6.useCallback)(async (valToCommit) => {
1365
+ if (isCommittingRef.current) return false;
1366
+ if (!isDirtyRef.current) return true;
1367
+ if (valToCommit === serverValueRef.current) {
1368
+ setIsDirty(false);
1369
+ isDirtyRef.current = false;
1370
+ setValidationError(null);
1371
+ lastFailedValRef.current = void 0;
1372
+ return true;
1373
+ }
1374
+ const valRes = validateCellValue(valToCommit, columnRef.current);
1375
+ if (!valRes.isValid) {
1376
+ const errorMsg = valRes.error || "\u05E9\u05D2\u05D9\u05D0\u05EA \u05D0\u05D9\u05DE\u05D5\u05EA";
1377
+ setValidationError(errorMsg);
1378
+ if (lastFailedValRef.current !== valToCommit) {
1379
+ addToast({ type: "error", message: errorMsg });
1380
+ lastFailedValRef.current = valToCommit;
1381
+ }
1382
+ return false;
1383
+ }
1384
+ try {
1385
+ isCommittingRef.current = true;
1386
+ const success = await onCommitRef.current(recordIdRef.current, columnRef.current.name, valToCommit);
1387
+ if (success) {
1388
+ setIsDirty(false);
1389
+ isDirtyRef.current = false;
1390
+ serverValueRef.current = valToCommit;
1391
+ setValidationError(null);
1392
+ lastFailedValRef.current = void 0;
1393
+ return true;
1394
+ } else {
1395
+ setValidationError("\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E9\u05DE\u05D9\u05E8\u05EA \u05D4\u05E8\u05E9\u05D5\u05DE\u05D4");
1396
+ return false;
1397
+ }
1398
+ } catch (e) {
1399
+ setValidationError("\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E9\u05DE\u05D9\u05E8\u05EA \u05D4\u05E8\u05E9\u05D5\u05DE\u05D4");
1400
+ return false;
1401
+ } finally {
1402
+ isCommittingRef.current = false;
1403
+ }
1404
+ }, [addToast]);
1405
+ const discard = (0, import_react6.useCallback)(() => {
1406
+ setDraftValue(serverValueRef.current);
1407
+ draftRef.current = serverValueRef.current;
1408
+ setIsDirty(false);
1409
+ isDirtyRef.current = false;
1410
+ setValidationError(null);
1411
+ lastFailedValRef.current = void 0;
1412
+ }, []);
1413
+ (0, import_react6.useEffect)(() => {
1414
+ return () => {
1415
+ if (isDirtyRef.current && !isCommittingRef.current) {
1416
+ const val = draftRef.current;
1417
+ const orig = serverValueRef.current;
1418
+ if (val !== orig) {
1419
+ const valRes = validateCellValue(val, columnRef.current);
1420
+ if (valRes.isValid) {
1421
+ onCommitRef.current(recordIdRef.current, columnRef.current.name, val);
1422
+ }
1423
+ }
1424
+ }
1425
+ };
1426
+ }, []);
1427
+ const handleChange = (newVal) => {
1428
+ setDraftValue(newVal);
1429
+ draftRef.current = newVal;
1430
+ setIsDirty(true);
1431
+ isDirtyRef.current = true;
1432
+ if (validationError) {
1433
+ setValidationError(null);
1434
+ lastFailedValRef.current = void 0;
1435
+ }
1436
+ if (colType === "BOOLEAN" || colType === "GOOGLE_DRIVE_FILE") {
1437
+ commit(newVal);
1438
+ }
1439
+ };
1440
+ const handleBlur = (e) => {
1441
+ if (e.currentTarget.contains(e.relatedTarget)) {
1442
+ return;
1443
+ }
1444
+ if (isDirtyRef.current) {
1445
+ commit(draftRef.current);
1446
+ }
1447
+ };
1448
+ const handleKeyDown = (e) => {
1449
+ var _a2, _b2, _c, _d;
1450
+ if (e.key === "Enter") {
1451
+ e.preventDefault();
1452
+ if (isDirtyRef.current) {
1453
+ commit(draftRef.current);
1454
+ }
1455
+ (_b2 = (_a2 = e.target) == null ? void 0 : _a2.blur) == null ? void 0 : _b2.call(_a2);
1456
+ } else if (e.key === "Escape") {
1457
+ e.preventDefault();
1458
+ discard();
1459
+ (_d = (_c = e.target) == null ? void 0 : _c.blur) == null ? void 0 : _d.call(_c);
1460
+ }
1461
+ };
1462
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1463
+ "td",
1464
+ {
1465
+ ref: cellRef,
1466
+ onBlur: handleBlur,
1467
+ onKeyDown: handleKeyDown,
1468
+ title: validationError || void 0,
1469
+ className: `border border-slate-200 p-0 relative h-8 transition-colors ${isContact ? "min-w-[140px]" : ""} ${validationError ? "border-rose-400 ring-1 ring-rose-400 bg-rose-50/20" : ""}`,
1470
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1471
+ InputComponent,
1472
+ {
1473
+ value: draftValue,
1474
+ column,
1475
+ rowId: record.id,
1476
+ onChange: handleChange,
1477
+ context: __spreadProps(__spreadValues({}, context), {
1478
+ 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",
1479
+ relationOptions,
1480
+ updateRelationOptions: (name, opts) => setRelationOptions((prev) => __spreadProps(__spreadValues({}, prev), { [name]: opts })),
1481
+ refetchRelation,
1482
+ parentRecord: { tableId: (context == null ? void 0 : context.parentTableId) || (context == null ? void 0 : context.tableId), data: context == null ? void 0 : context.rowData },
1483
+ rowData: record.data,
1484
+ tableId: (targetSchema == null ? void 0 : targetSchema.tableMetaId) || (targetSchema == null ? void 0 : targetSchema.id) || referencedTableId,
1485
+ updateQuietly: (rid, colName, val) => onCommitRef.current(rid, colName, val)
1486
+ })
1487
+ }
1488
+ )
1489
+ }
1490
+ );
1491
+ }
1492
+ function SubTableViewer({ rowId, column, value, onChange, context }) {
1493
+ var _a, _b;
1191
1494
  const { addToast } = useToast();
1192
1495
  const entries = Array.isArray(value) ? value : [];
1193
- const [linkedRecords, setLinkedRecords] = (0, import_react6.useState)([]);
1496
+ const [linkedRecords, setLinkedRecords] = (0, import_react6.useState)(() => {
1497
+ const initial = [];
1498
+ if (Array.isArray(value)) {
1499
+ for (const entry of value) {
1500
+ if (entry.recordId && entry._resolvedData) {
1501
+ initial.push({ id: entry.recordId, data: entry._resolvedData });
1502
+ }
1503
+ }
1504
+ }
1505
+ return initial;
1506
+ });
1194
1507
  const [targetSchema, setTargetSchema] = (0, import_react6.useState)(null);
1195
1508
  const [isLoading, setIsLoading] = (0, import_react6.useState)(true);
1196
1509
  const [isSearchOpen, setIsSearchOpen] = (0, import_react6.useState)(false);
@@ -1199,11 +1512,31 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
1199
1512
  const [isSearching, setIsSearching] = (0, import_react6.useState)(false);
1200
1513
  const [isCreating, setIsCreating] = (0, import_react6.useState)(false);
1201
1514
  const [newRecordData, setNewRecordData] = (0, import_react6.useState)({});
1515
+ const [conflictModalState, setConflictModalState] = (0, import_react6.useState)({
1516
+ isOpen: false,
1517
+ fieldName: "",
1518
+ matchingRecords: []
1519
+ });
1202
1520
  const searchRef = (0, import_react6.useRef)(null);
1203
1521
  const searchTimerRef = (0, import_react6.useRef)(null);
1204
1522
  const executeAction = context == null ? void 0 : context.executeAction;
1205
1523
  const referencedTableId = column.referencedTableId;
1206
1524
  const fetchedIdsRef = (0, import_react6.useRef)(/* @__PURE__ */ new Set());
1525
+ (0, import_react6.useEffect)(() => {
1526
+ if (entries.length > 0) {
1527
+ setLinkedRecords((prev) => {
1528
+ const existingIds = new Set(prev.map((r) => r.id));
1529
+ const toAdd = [];
1530
+ for (const entry of entries) {
1531
+ if (entry.recordId && entry._resolvedData && !existingIds.has(entry.recordId)) {
1532
+ toAdd.push({ id: entry.recordId, data: entry._resolvedData });
1533
+ existingIds.add(entry.recordId);
1534
+ }
1535
+ }
1536
+ return toAdd.length > 0 ? [...prev, ...toAdd] : prev;
1537
+ });
1538
+ }
1539
+ }, [entries]);
1207
1540
  (0, import_react6.useEffect)(() => {
1208
1541
  if (!referencedTableId || !executeAction) {
1209
1542
  setIsLoading(false);
@@ -1329,20 +1662,102 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
1329
1662
  }
1330
1663
  }
1331
1664
  };
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);
1665
+ const handleLinkRecords = (recordsToLink) => {
1666
+ if (!recordsToLink || recordsToLink.length === 0) return;
1667
+ const existingEntryIds = new Set(entries.map((e) => e.recordId));
1668
+ const newEntries = [];
1669
+ const newRecordsToAdd = [];
1670
+ for (const record of recordsToLink) {
1671
+ fetchedIdsRef.current.add(record.id);
1672
+ const resolvedData = __spreadValues({}, record.data || {});
1673
+ targetColumns.forEach((col) => {
1674
+ if ((resolvedData[col.name] === void 0 || resolvedData[col.name] === null) && record[col.name] !== void 0) {
1675
+ resolvedData[col.name] = record[col.name];
1676
+ }
1677
+ });
1678
+ if (!existingEntryIds.has(record.id)) {
1679
+ newEntries.push({
1680
+ recordId: record.id,
1681
+ isSelected: true,
1682
+ _resolvedData: resolvedData
1683
+ });
1684
+ existingEntryIds.add(record.id);
1685
+ }
1686
+ newRecordsToAdd.push({
1687
+ id: record.id,
1688
+ data: resolvedData
1689
+ });
1690
+ }
1691
+ const updatedEntries = [...entries, ...newEntries];
1692
+ updateParentLinkage(updatedEntries);
1337
1693
  setLinkedRecords((prev) => {
1338
- const exists = prev.find((r) => r.id === record.id);
1339
- if (exists) return prev;
1340
- return [...prev, record];
1694
+ const map = new Map(prev.map((r) => [r.id, r]));
1695
+ newRecordsToAdd.forEach((r) => {
1696
+ const existing = map.get(r.id);
1697
+ if (existing) {
1698
+ map.set(r.id, __spreadProps(__spreadValues({}, existing), {
1699
+ data: __spreadValues(__spreadValues({}, existing.data), r.data)
1700
+ }));
1701
+ } else {
1702
+ map.set(r.id, r);
1703
+ }
1704
+ });
1705
+ return Array.from(map.values());
1341
1706
  });
1342
1707
  setSearchQuery("");
1343
1708
  setSearchResults([]);
1344
1709
  setIsSearchOpen(false);
1345
1710
  };
1711
+ const handleLinkRecord = (record) => {
1712
+ handleLinkRecords([record]);
1713
+ };
1714
+ const handleUniqueConflict = (conflictData) => {
1715
+ const rawMatching = Array.isArray(conflictData == null ? void 0 : conflictData.matchingRecords) ? conflictData.matchingRecords : [];
1716
+ const resolvedMatching = rawMatching.map((r) => {
1717
+ var _a2;
1718
+ const recordId = String(r.id || ((_a2 = r._id) == null ? void 0 : _a2.$oid) || r._id);
1719
+ let dataObj = {};
1720
+ if (r.data && typeof r.data === "object") {
1721
+ dataObj = __spreadValues({}, r.data);
1722
+ }
1723
+ targetColumns.forEach((col) => {
1724
+ if ((dataObj[col.name] === void 0 || dataObj[col.name] === null) && r[col.name] !== void 0) {
1725
+ dataObj[col.name] = r[col.name];
1726
+ }
1727
+ });
1728
+ return {
1729
+ id: recordId,
1730
+ data: dataObj
1731
+ };
1732
+ });
1733
+ setConflictModalState({
1734
+ isOpen: true,
1735
+ fieldName: (conflictData == null ? void 0 : conflictData.fieldName) || "",
1736
+ matchingRecords: resolvedMatching
1737
+ });
1738
+ };
1739
+ const handleCancelConflict = () => {
1740
+ setConflictModalState((prev) => __spreadProps(__spreadValues({}, prev), { isOpen: false }));
1741
+ };
1742
+ const handleConfirmLinkConflict = async () => {
1743
+ const { matchingRecords } = conflictModalState;
1744
+ if (matchingRecords && matchingRecords.length > 0) {
1745
+ handleLinkRecords(matchingRecords);
1746
+ setNewRecordData({});
1747
+ setIsCreating(false);
1748
+ setConflictModalState({
1749
+ isOpen: false,
1750
+ fieldName: "",
1751
+ matchingRecords: []
1752
+ });
1753
+ addToast({
1754
+ type: "success",
1755
+ message: "\u05D4\u05E8\u05E9\u05D5\u05DE\u05D4 \u05E7\u05D5\u05E9\u05E8\u05D4 \u05D1\u05D4\u05E6\u05DC\u05D7\u05D4"
1756
+ });
1757
+ } else {
1758
+ setConflictModalState((prev) => __spreadProps(__spreadValues({}, prev), { isOpen: false }));
1759
+ }
1760
+ };
1346
1761
  const handleUnlinkRecord = (recordId) => {
1347
1762
  const updated = entries.filter((e) => e.recordId !== recordId);
1348
1763
  updateParentLinkage(updated);
@@ -1355,37 +1770,94 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
1355
1770
  );
1356
1771
  updateParentLinkage(updated);
1357
1772
  };
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);
1773
+ const isMountedRef = (0, import_react6.useRef)(true);
1774
+ (0, import_react6.useEffect)(() => {
1775
+ isMountedRef.current = true;
1776
+ return () => {
1777
+ isMountedRef.current = false;
1778
+ };
1779
+ }, []);
1780
+ const handleCommitRecordCell = async (recordId, colName, value2) => {
1781
+ var _a2;
1782
+ const previousRecord = linkedRecords.find((r) => r.id === recordId);
1783
+ const previousValue = (_a2 = previousRecord == null ? void 0 : previousRecord.data) == null ? void 0 : _a2[colName];
1784
+ if (previousValue === value2) return true;
1785
+ const targetCol = targetColumns.find((c) => c.name === colName);
1786
+ const valRes = targetCol ? validateCellValue(value2, targetCol) : { isValid: true };
1787
+ if (!valRes.isValid) {
1788
+ addToast({
1789
+ type: "error",
1790
+ message: valRes.error || "\u05E9\u05D2\u05D9\u05D0\u05EA \u05D0\u05D9\u05DE\u05D5\u05EA"
1791
+ });
1792
+ return false;
1371
1793
  }
1372
1794
  if (executeAction && referencedTableId && !recordId.startsWith("temp_")) {
1373
1795
  try {
1374
- await executeAction("UPDATE_RECORD", {
1796
+ const res = await executeAction("UPDATE_RECORD", {
1375
1797
  tableId: referencedTableId,
1376
1798
  recordId,
1377
1799
  data: { [colName]: value2 }
1378
1800
  });
1379
- if (context == null ? void 0 : context.triggerSaveSuccess) {
1380
- context.triggerSaveSuccess();
1801
+ if (res && res.success === false) {
1802
+ const err = new Error(res.error || res.message || "Action execution failed");
1803
+ Object.assign(err, res);
1804
+ throw err;
1805
+ }
1806
+ if (isMountedRef.current) {
1807
+ const updatedRecords = linkedRecords.map(
1808
+ (r) => r.id === recordId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value2 }) }) : r
1809
+ );
1810
+ setLinkedRecords(updatedRecords);
1811
+ const targetRecord = updatedRecords.find((r) => r.id === recordId);
1812
+ const updatedEntries = entries.map(
1813
+ (e) => e.recordId === recordId ? __spreadProps(__spreadValues({}, e), { _resolvedData: targetRecord == null ? void 0 : targetRecord.data }) : e
1814
+ );
1815
+ if (context == null ? void 0 : context.updateQuietly) {
1816
+ context.updateQuietly(rowId, column.name, updatedEntries);
1817
+ } else {
1818
+ onChange(updatedEntries);
1819
+ }
1820
+ if (context == null ? void 0 : context.triggerSaveSuccess) {
1821
+ context.triggerSaveSuccess();
1822
+ }
1381
1823
  }
1824
+ return true;
1382
1825
  } catch (err) {
1383
1826
  console.error("[SubTableViewer] Failed to auto-save linked record field", err);
1827
+ let errorMsg = (err == null ? void 0 : err.message) || (err == null ? void 0 : err.error) || "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05E9\u05DE\u05D9\u05E8\u05EA \u05D4\u05E8\u05E9\u05D5\u05DE\u05D4";
1828
+ if ((err == null ? void 0 : err.error) === "UNIQUE_CONSTRAINT_CONFLICT" || (err == null ? void 0 : err.code) === "UNIQUE_CONSTRAINT_CONFLICT" || errorMsg.includes("must be unique") || errorMsg.includes("Duplicate value detected") || errorMsg.includes("\u05DB\u05D1\u05E8 \u05E7\u05D9\u05D9\u05DE\u05EA") || errorMsg.includes("UNIQUE_CONSTRAINT_CONFLICT")) {
1829
+ errorMsg = `\u05E9\u05D2\u05D9\u05D0\u05EA \u05D0\u05D9\u05DE\u05D5\u05EA: \u05D4\u05E2\u05E8\u05DA '${value2}' \u05DB\u05D1\u05E8 \u05E7\u05D9\u05D9\u05DD \u05D1\u05E9\u05D3\u05D4 '${colName}' (\u05E9\u05D3\u05D4 \u05D9\u05D9\u05D7\u05D5\u05D3\u05D9).`;
1830
+ } else if (errorMsg.includes("is required") || errorMsg.includes("\u05E9\u05D3\u05D4 \u05D7\u05D5\u05D1\u05D4") || errorMsg.toLowerCase().includes("required")) {
1831
+ errorMsg = `\u05E9\u05D2\u05D9\u05D0\u05EA \u05D0\u05D9\u05DE\u05D5\u05EA: \u05E9\u05D3\u05D4 '${colName}' \u05D4\u05D9\u05E0\u05D5 \u05E9\u05D3\u05D4 \u05D7\u05D5\u05D1\u05D4 \u05D5\u05DC\u05D0 \u05D9\u05DB\u05D5\u05DC \u05DC\u05D4\u05D9\u05E9\u05D0\u05E8 \u05E8\u05D9\u05E7.`;
1832
+ }
1833
+ addToast({
1834
+ type: "error",
1835
+ message: errorMsg
1836
+ });
1837
+ return false;
1384
1838
  }
1839
+ } else {
1840
+ if (isMountedRef.current) {
1841
+ const updatedRecords = linkedRecords.map(
1842
+ (r) => r.id === recordId ? __spreadProps(__spreadValues({}, r), { data: __spreadProps(__spreadValues({}, r.data), { [colName]: value2 }) }) : r
1843
+ );
1844
+ setLinkedRecords(updatedRecords);
1845
+ const targetRecord = updatedRecords.find((r) => r.id === recordId);
1846
+ const updatedEntries = entries.map(
1847
+ (e) => e.recordId === recordId ? __spreadProps(__spreadValues({}, e), { _resolvedData: targetRecord == null ? void 0 : targetRecord.data }) : e
1848
+ );
1849
+ if (context == null ? void 0 : context.updateQuietly) {
1850
+ context.updateQuietly(rowId, column.name, updatedEntries);
1851
+ } else {
1852
+ onChange(updatedEntries);
1853
+ }
1854
+ }
1855
+ return true;
1385
1856
  }
1386
1857
  };
1858
+ const handleRecordCellChange = handleCommitRecordCell;
1387
1859
  const handleCreateRecord = async () => {
1388
- var _a2;
1860
+ var _a2, _b2, _c, _d, _e;
1389
1861
  if (!executeAction || !referencedTableId) return;
1390
1862
  for (const col of targetColumns) {
1391
1863
  if (col.isRequired) {
@@ -1398,16 +1870,30 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
1398
1870
  return;
1399
1871
  }
1400
1872
  }
1873
+ if (((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) === "PHONE") {
1874
+ const val = newRecordData[col.name];
1875
+ if (val !== void 0 && val !== null && String(val).trim() !== "" && !isValidPhoneNumber(String(val))) {
1876
+ addToast({
1877
+ type: "error",
1878
+ message: `\u05DE\u05E1\u05E4\u05E8 \u05D4\u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05E9\u05D3\u05D4 '${col.name}' \u05D0\u05D9\u05E0\u05D5 \u05EA\u05E7\u05D9\u05DF. \u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05DE\u05E1\u05E4\u05E8 \u05D1\u05D0\u05D5\u05E8\u05DA 9-15 \u05E1\u05E4\u05E8\u05D5\u05EA.`
1879
+ });
1880
+ return;
1881
+ }
1882
+ }
1401
1883
  }
1402
1884
  try {
1403
1885
  const res = await executeAction("CREATE_RECORD", {
1404
1886
  tableId: referencedTableId,
1405
1887
  records: [newRecordData]
1406
1888
  });
1889
+ if (res && (res.error === "UNIQUE_CONSTRAINT_CONFLICT" || res.code === "UNIQUE_CONSTRAINT_CONFLICT")) {
1890
+ handleUniqueConflict(res);
1891
+ return;
1892
+ }
1407
1893
  if (res && res.success === false) {
1408
1894
  throw new Error(res.error || "Action execution failed");
1409
1895
  }
1410
- const created = Array.isArray(res) ? res[0] : ((_a2 = res == null ? void 0 : res.data) == null ? void 0 : _a2[0]) || res;
1896
+ const created = Array.isArray(res) ? res[0] : ((_b2 = res == null ? void 0 : res.data) == null ? void 0 : _b2[0]) || res;
1411
1897
  if (created) {
1412
1898
  const newRecord = {
1413
1899
  id: String(created.id || created._id),
@@ -1418,6 +1904,12 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
1418
1904
  setNewRecordData({});
1419
1905
  }
1420
1906
  } catch (err) {
1907
+ const isConflict = (err == null ? void 0 : err.error) === "UNIQUE_CONSTRAINT_CONFLICT" || (err == null ? void 0 : err.code) === "UNIQUE_CONSTRAINT_CONFLICT" || ((_c = err == null ? void 0 : err.data) == null ? void 0 : _c.error) === "UNIQUE_CONSTRAINT_CONFLICT" || ((_d = err == null ? void 0 : err.message) == null ? void 0 : _d.includes("UNIQUE_CONSTRAINT_CONFLICT"));
1908
+ if (isConflict) {
1909
+ const conflictPayload = (err == null ? void 0 : err.error) === "UNIQUE_CONSTRAINT_CONFLICT" ? err : ((_e = err == null ? void 0 : err.data) == null ? void 0 : _e.error) === "UNIQUE_CONSTRAINT_CONFLICT" ? err.data : err;
1910
+ handleUniqueConflict(conflictPayload);
1911
+ return;
1912
+ }
1421
1913
  const msg = (err == null ? void 0 : err.message) || String(err);
1422
1914
  addToast({
1423
1915
  type: "error",
@@ -1504,11 +1996,23 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
1504
1996
  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
1997
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { className: "bg-slate-100/80", children: [
1506
1998
  /* @__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)),
1999
+ targetColumns.map((col) => {
2000
+ var _a2;
2001
+ const isContact = ((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) === "GOOGLE_CONTACT";
2002
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2003
+ "th",
2004
+ {
2005
+ className: `border border-slate-200 px-2 py-1.5 text-right font-semibold text-slate-600 whitespace-nowrap ${isContact ? "min-w-[140px]" : ""}`,
2006
+ children: col.name
2007
+ },
2008
+ col.name
2009
+ );
2010
+ }),
1508
2011
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("th", { className: "border border-slate-200 px-2 py-1.5 w-14" })
1509
2012
  ] }) }),
1510
2013
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tbody", { children: [
1511
2014
  entries.map((entry) => {
2015
+ var _a2;
1512
2016
  const record = linkedRecords.find((r) => r.id === entry.recordId);
1513
2017
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { className: "hover:bg-blue-50/30 transition-colors", children: [
1514
2018
  /* @__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)(
@@ -1521,61 +2025,101 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
1521
2025
  }
1522
2026
  ) }),
1523
2027
  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,
2028
+ var _a3;
2029
+ const isContact = ((_a3 = col.type) == null ? void 0 : _a3.toUpperCase()) === "GOOGLE_CONTACT";
2030
+ if (!record) {
2031
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2032
+ "td",
2033
+ {
2034
+ className: `border border-slate-200 p-0 relative h-8 ${isContact ? "min-w-[140px]" : ""}`,
2035
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-slate-300 italic px-2", children: "\u05D8\u05D5\u05E2\u05DF..." })
2036
+ },
2037
+ col.name
2038
+ );
2039
+ }
2040
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2041
+ SubTableCell,
1529
2042
  {
1530
- value: (_b = record.data[col.name]) != null ? _b : colType === "BOOLEAN" ? false : "",
2043
+ record,
1531
2044
  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);
2045
+ targetSchema,
2046
+ referencedTableId,
2047
+ context,
2048
+ relationOptions,
2049
+ setRelationOptions,
2050
+ refetchRelation,
2051
+ onCommit: handleCommitRecordCell
2052
+ },
2053
+ `${record.id}_${col.name}`
2054
+ );
1546
2055
  }),
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
- ) })
2056
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("td", { className: "border border-slate-200 px-1 py-1 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
2057
+ ((_a2 = column.config) == null ? void 0 : _a2.actionType) === "WHATSAPP" && (() => {
2058
+ var _a3, _b2, _c;
2059
+ const actionPhoneCol = (_a3 = column.config) == null ? void 0 : _a3.actionPhoneColumn;
2060
+ const phone = actionPhoneCol ? ((_b2 = record == null ? void 0 : record.data) == null ? void 0 : _b2[actionPhoneCol]) || ((_c = entry._resolvedData) == null ? void 0 : _c[actionPhoneCol]) : null;
2061
+ if (phone) {
2062
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2063
+ "button",
2064
+ {
2065
+ type: "button",
2066
+ onClick: (e) => {
2067
+ e.stopPropagation();
2068
+ openWhatsAppChat(String(phone));
2069
+ },
2070
+ className: "text-emerald-500 hover:text-emerald-600 p-0.5 rounded hover:bg-emerald-50 transition-colors cursor-pointer",
2071
+ title: `\u05E4\u05EA\u05D9\u05D7\u05EA \u05D5\u05D5\u05D0\u05D8\u05E1\u05D0\u05E4 (${phone})`,
2072
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-3.5 h-3.5", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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" }) })
2073
+ }
2074
+ );
2075
+ }
2076
+ return null;
2077
+ })(),
2078
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2079
+ "button",
2080
+ {
2081
+ type: "button",
2082
+ onClick: (e) => {
2083
+ e.stopPropagation();
2084
+ handleUnlinkRecord(entry.recordId);
2085
+ },
2086
+ className: "text-slate-400 hover:text-rose-500 p-0.5 rounded transition-colors cursor-pointer inline-flex items-center justify-center",
2087
+ title: "\u05D4\u05E1\u05E8 \u05E7\u05D9\u05E9\u05D5\u05E8",
2088
+ 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" }) })
2089
+ }
2090
+ )
2091
+ ] }) })
1557
2092
  ] }, entry.recordId);
1558
2093
  }),
1559
2094
  isCreating && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("tr", { className: "bg-emerald-50/40", children: [
1560
2095
  /* @__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
2096
  targetColumns.map((col) => {
1562
- var _a2, _b;
2097
+ var _a2, _b2;
1563
2098
  const colType = ((_a2 = col.type) == null ? void 0 : _a2.toUpperCase()) || "DEFAULT";
1564
2099
  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,
2100
+ const isContact = colType === "GOOGLE_CONTACT";
2101
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2102
+ "td",
1567
2103
  {
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}`);
2104
+ className: `border border-emerald-200 p-0 relative h-8 ${isContact ? "min-w-[140px]" : ""}`,
2105
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2106
+ InputComponent,
2107
+ {
2108
+ value: (_b2 = newRecordData[col.name]) != null ? _b2 : colType === "BOOLEAN" ? false : "",
2109
+ column: col,
2110
+ rowId: "new_record",
2111
+ onChange: (v) => setNewRecordData((prev) => __spreadProps(__spreadValues({}, prev), { [col.name]: v })),
2112
+ context: __spreadProps(__spreadValues({}, context), {
2113
+ 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",
2114
+ relationOptions,
2115
+ refetchRelation,
2116
+ tableId: (targetSchema == null ? void 0 : targetSchema.tableMetaId) || (targetSchema == null ? void 0 : targetSchema.id) || referencedTableId
2117
+ })
2118
+ }
2119
+ )
2120
+ },
2121
+ `new_${col.name}`
2122
+ );
1579
2123
  }),
1580
2124
  /* @__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
2125
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -1603,7 +2147,102 @@ function SubTableViewer({ rowId, column, value, onChange, context }) {
1603
2147
  ] }) })
1604
2148
  ] })
1605
2149
  ] })
1606
- ] }) })
2150
+ ] }) }),
2151
+ conflictModalState.isOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2152
+ "div",
2153
+ {
2154
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-sm animate-in fade-in duration-200",
2155
+ dir: "rtl",
2156
+ onClick: handleCancelConflict,
2157
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2158
+ "div",
2159
+ {
2160
+ className: "bg-white rounded-xl shadow-2xl w-full max-w-lg sm:max-w-xl overflow-hidden border border-slate-200 animate-in fade-in zoom-in-95 duration-200",
2161
+ onClick: (e) => e.stopPropagation(),
2162
+ children: [
2163
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-5 border-b border-slate-100 flex items-center gap-3", children: [
2164
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-10 h-10 rounded-full bg-blue-50 border border-blue-200 flex items-center justify-center text-blue-600 flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" }) }) }),
2165
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
2166
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "text-base font-bold text-slate-800", children: conflictModalState.matchingRecords.length > 1 ? "\u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05E7\u05D9\u05D9\u05DE\u05D5\u05EA \u05D1\u05D8\u05D1\u05DC\u05D4" : "\u05E8\u05E9\u05D5\u05DE\u05D4 \u05E7\u05D9\u05D9\u05DE\u05EA \u05D1\u05D8\u05D1\u05DC\u05D4" }),
2167
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-xs text-slate-500", children: "\u05E0\u05DE\u05E6\u05D0\u05D4 \u05E8\u05E9\u05D5\u05DE\u05D4 \u05E7\u05D9\u05D9\u05DE\u05EA \u05E2\u05DD \u05D0\u05D5\u05EA\u05D5 \u05E2\u05E8\u05DA \u05D9\u05D9\u05D7\u05D5\u05D3\u05D9" })
2168
+ ] })
2169
+ ] }),
2170
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-5 space-y-3", children: [
2171
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "text-sm text-slate-700 leading-relaxed", children: [
2172
+ "\u05E7\u05D9\u05D9\u05DE\u05EA \u05DB\u05D1\u05E8 \u05E8\u05E9\u05D5\u05DE\u05D4 \u05E2\u05DD \u05D4\u05E2\u05E8\u05DA \u05E9\u05E0\u05D1\u05D7\u05E8 \u05D1\u05E9\u05D3\u05D4",
2173
+ " ",
2174
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-bold text-slate-900 bg-slate-100 px-1.5 py-0.5 rounded border border-slate-200", children: conflictModalState.fieldName }),
2175
+ ". \u05D4\u05D0\u05DD \u05EA\u05E8\u05E6\u05D4 \u05DC\u05E7\u05E9\u05E8 \u05D0\u05D5\u05EA\u05D4 \u05DC\u05EA\u05EA-\u05D4\u05D8\u05D1\u05DC\u05D4 \u05D1\u05DE\u05E7\u05D5\u05DD \u05DC\u05D9\u05E6\u05D5\u05E8 \u05D7\u05D3\u05E9\u05D4?"
2176
+ ] }),
2177
+ conflictModalState.matchingRecords.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-3 bg-blue-50 border border-blue-200 rounded-lg text-xs font-semibold text-blue-800", children: [
2178
+ "\u05E0\u05DE\u05E6\u05D0\u05D5 ",
2179
+ conflictModalState.matchingRecords.length,
2180
+ " \u05E8\u05E9\u05D5\u05DE\u05D5\u05EA \u05E7\u05D9\u05D9\u05DE\u05D5\u05EA. \u05D4\u05D0\u05DD \u05DC\u05E7\u05E9\u05E8 \u05D0\u05EA \u05DB\u05D5\u05DC\u05DF?"
2181
+ ] }),
2182
+ conflictModalState.matchingRecords.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "overflow-x-auto border border-slate-200 rounded-lg max-h-48 text-xs bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("table", { className: "w-full text-right border-collapse", children: [
2183
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tr", { className: "bg-slate-100 text-slate-700 font-semibold border-b whitespace-nowrap", children: (targetColumns.length > 0 ? targetColumns.filter((c) => c.name !== "_id" && c.name !== "id") : Object.keys(((_b = conflictModalState.matchingRecords[0]) == null ? void 0 : _b.data) || {}).filter((k) => k !== "_id" && k !== "id").map((name) => ({ name }))).map((col) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2184
+ "th",
2185
+ {
2186
+ className: "bg-slate-100 text-slate-700 font-semibold p-2 border-b whitespace-nowrap",
2187
+ children: col.name
2188
+ },
2189
+ col.name
2190
+ )) }) }),
2191
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tbody", { children: conflictModalState.matchingRecords.map((r, idx) => {
2192
+ const cols = targetColumns.length > 0 ? targetColumns.filter((c) => c.name !== "_id" && c.name !== "id") : Object.keys(r.data || {}).filter((k) => k !== "_id" && k !== "id").map((name) => ({ name }));
2193
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tr", { className: "hover:bg-slate-50/80 transition-colors", children: cols.map((col) => {
2194
+ var _a2;
2195
+ const rawVal = (_a2 = r.data) == null ? void 0 : _a2[col.name];
2196
+ let displayVal = "-";
2197
+ if (rawVal !== void 0 && rawVal !== null && rawVal !== "") {
2198
+ if (typeof rawVal === "boolean") {
2199
+ displayVal = rawVal ? "\u05DB\u05DF" : "\u05DC\u05D0";
2200
+ } else if (typeof rawVal === "object") {
2201
+ displayVal = Array.isArray(rawVal) ? rawVal.join(", ") : JSON.stringify(rawVal);
2202
+ } else {
2203
+ displayVal = String(rawVal);
2204
+ }
2205
+ }
2206
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2207
+ "td",
2208
+ {
2209
+ className: "p-2 border-b border-slate-100 whitespace-nowrap text-slate-800",
2210
+ children: displayVal
2211
+ },
2212
+ col.name
2213
+ );
2214
+ }) }, r.id || idx);
2215
+ }) })
2216
+ ] }) })
2217
+ ] }),
2218
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-4 bg-slate-50 border-t border-slate-100 flex items-center justify-end gap-2.5", children: [
2219
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2220
+ "button",
2221
+ {
2222
+ type: "button",
2223
+ onClick: handleCancelConflict,
2224
+ className: "px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-200/70 rounded-lg border border-slate-300 transition-colors cursor-pointer",
2225
+ children: "\u05D1\u05D9\u05D8\u05D5\u05DC"
2226
+ }
2227
+ ),
2228
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2229
+ "button",
2230
+ {
2231
+ type: "button",
2232
+ onClick: handleConfirmLinkConflict,
2233
+ className: "px-4 py-2 text-sm font-semibold text-white bg-blue-600 hover:bg-blue-700 rounded-lg shadow transition-colors flex items-center gap-1.5 cursor-pointer",
2234
+ children: [
2235
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" }) }),
2236
+ conflictModalState.matchingRecords.length > 1 ? "\u05E7\u05E9\u05E8 \u05D0\u05EA \u05DB\u05D5\u05DC\u05DF" : "\u05E7\u05E9\u05E8 \u05E8\u05E9\u05D5\u05DE\u05D4"
2237
+ ]
2238
+ }
2239
+ )
2240
+ ] })
2241
+ ]
2242
+ }
2243
+ )
2244
+ }
2245
+ )
1607
2246
  ] });
1608
2247
  }
1609
2248
 
@@ -2209,7 +2848,7 @@ function TableBlockClient({
2209
2848
  const formatPayload = (payload) => {
2210
2849
  const formatted = {};
2211
2850
  (localColumns || []).forEach((col) => {
2212
- var _a, _b;
2851
+ var _a, _b, _c;
2213
2852
  if (["_id", "id", "createdAt", "updatedAt", "orgId", "tableId", "isSelected"].includes(col.name)) return;
2214
2853
  let val = payload[col.name];
2215
2854
  if (val !== void 0) {
@@ -2219,7 +2858,9 @@ function TableBlockClient({
2219
2858
  if (((_a = col.type) == null ? void 0 : _a.toUpperCase()) === "NUMBER") {
2220
2859
  val = val === "" ? 0 : Number(val);
2221
2860
  if (isNaN(val)) val = 0;
2222
- } else if (((_b = col.type) == null ? void 0 : _b.toUpperCase()) === "SUB_TABLE" && Array.isArray(val)) {
2861
+ } else if (((_b = col.type) == null ? void 0 : _b.toUpperCase()) === "PHONE") {
2862
+ val = sanitizePhoneNumber(String(val));
2863
+ } else if (((_c = col.type) == null ? void 0 : _c.toUpperCase()) === "SUB_TABLE" && Array.isArray(val)) {
2223
2864
  val = val.map((item) => {
2224
2865
  const _a2 = item, { _resolvedData } = _a2, rest = __objRest(_a2, ["_resolvedData"]);
2225
2866
  return rest;
@@ -2233,7 +2874,7 @@ function TableBlockClient({
2233
2874
  };
2234
2875
  const hasChanges = dirty.added.length > 0 || Object.keys(dirty.modified).length > 0 || dirty.deleted.length > 0;
2235
2876
  const handleSave = async () => {
2236
- var _a;
2877
+ var _a, _b;
2237
2878
  if (!hasChanges) {
2238
2879
  return;
2239
2880
  }
@@ -2256,6 +2897,28 @@ function TableBlockClient({
2256
2897
  }
2257
2898
  }
2258
2899
  }
2900
+ const phoneCols = (localColumns || []).filter((c) => {
2901
+ var _a2;
2902
+ return ((_a2 = c.type) == null ? void 0 : _a2.toUpperCase()) === "PHONE";
2903
+ });
2904
+ for (const col of phoneCols) {
2905
+ for (const row of dirty.added) {
2906
+ const val = row.data[col.name];
2907
+ if (!isEmpty(val) && !isValidPhoneNumber(val)) {
2908
+ showError(`\u05DE\u05E1\u05E4\u05E8 \u05D4\u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05E9\u05D3\u05D4 "${col.name}" \u05D0\u05D9\u05E0\u05D5 \u05EA\u05E7\u05D9\u05DF. \u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05DE\u05E1\u05E4\u05E8 \u05D1\u05D0\u05D5\u05E8\u05DA 9-15 \u05E1\u05E4\u05E8\u05D5\u05EA (\u05DC\u05D3\u05D5\u05D2\u05DE\u05D4 0501234567 \u05D0\u05D5 +972501234567).`, "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D5\u05D5\u05DC\u05D9\u05D3\u05E6\u05D9\u05D4");
2909
+ return;
2910
+ }
2911
+ }
2912
+ for (const [rowId, mods] of Object.entries(dirty.modified)) {
2913
+ const rowData = ((_b = data.find((r) => r.id === rowId)) == null ? void 0 : _b.data) || {};
2914
+ const mergedData = __spreadValues(__spreadValues({}, rowData), mods);
2915
+ const val = mergedData[col.name];
2916
+ if (!isEmpty(val) && !isValidPhoneNumber(val)) {
2917
+ showError(`\u05DE\u05E1\u05E4\u05E8 \u05D4\u05D8\u05DC\u05E4\u05D5\u05DF \u05D1\u05E9\u05D3\u05D4 "${col.name}" \u05D0\u05D9\u05E0\u05D5 \u05EA\u05E7\u05D9\u05DF. \u05D9\u05E9 \u05DC\u05D4\u05D6\u05D9\u05DF \u05DE\u05E1\u05E4\u05E8 \u05D1\u05D0\u05D5\u05E8\u05DA 9-15 \u05E1\u05E4\u05E8\u05D5\u05EA (\u05DC\u05D3\u05D5\u05D2\u05DE\u05D4 0501234567 \u05D0\u05D5 +972501234567).`, "\u05E9\u05D2\u05D9\u05D0\u05D4 \u05D1\u05D5\u05D5\u05DC\u05D9\u05D3\u05E6\u05D9\u05D4");
2918
+ return;
2919
+ }
2920
+ }
2921
+ }
2259
2922
  setIsSaving(true);
2260
2923
  try {
2261
2924
  if (!executeAction) {
@@ -2919,5 +3582,7 @@ var UNIVERSE_COMPONENTS = {
2919
3582
  ToastProvider,
2920
3583
  UNIVERSE_COMPONENTS,
2921
3584
  cn,
3585
+ formatPhoneDisplay,
3586
+ openWhatsAppChat,
2922
3587
  useToast
2923
3588
  });