@eztrak/shared 0.6.9 → 0.7.2

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
@@ -30,12 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ CustomCellEditor: () => CustomCellEditor,
33
34
  CustomPagination: () => CustomPagination,
34
35
  EztrakTabs: () => EztrakTabs,
35
36
  Loader: () => Loader,
36
37
  Modal: () => Modal,
37
38
  ResetColumnsButton: () => ResetColumnsButton,
38
39
  ResetFiltersButton: () => ResetFiltersButton,
40
+ SearchInput: () => SearchInput,
39
41
  TableLayoutToolbarControls: () => TableLayoutToolbarControls,
40
42
  capitalizeText: () => capitalizeText,
41
43
  checkIfExistsInArray: () => checkIfExistsInArray,
@@ -999,17 +1001,132 @@ function ResetFiltersButton({
999
1001
  );
1000
1002
  }
1001
1003
 
1004
+ // src/components/search-filters/SearchInput.tsx
1005
+ var import_react4 = require("react");
1006
+ var import_fi = require("react-icons/fi");
1007
+ var import_react_router_dom3 = require("react-router-dom");
1008
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1009
+ function SearchInput({
1010
+ placeholder = "Search...",
1011
+ searchBtn = "",
1012
+ searchClassName = "",
1013
+ searchWrapperClass = "",
1014
+ searchIconClassName = "",
1015
+ defaultParam = "query",
1016
+ showIcon = true,
1017
+ name = "search",
1018
+ type = "search",
1019
+ liveSearch = false,
1020
+ debounceDelay = 500,
1021
+ customIcon = null,
1022
+ defaultValue,
1023
+ ...props
1024
+ }) {
1025
+ const [searchParams, setSearchParams] = (0, import_react_router_dom3.useSearchParams)();
1026
+ const queryParam = searchParams.get(defaultParam) || defaultValue || "";
1027
+ const [inputValue, setInputValue] = (0, import_react4.useState)(queryParam);
1028
+ const debounceTimerRef = (0, import_react4.useRef)(null);
1029
+ const updateSearchParams = (searchQuery) => {
1030
+ if (searchQuery?.trim()) {
1031
+ setSearchParams((prev) => {
1032
+ prev.set(defaultParam, searchQuery.trim());
1033
+ return prev;
1034
+ });
1035
+ } else {
1036
+ setSearchParams((prev) => {
1037
+ prev.delete(defaultParam);
1038
+ return prev;
1039
+ });
1040
+ }
1041
+ };
1042
+ const handleSearch = (e) => {
1043
+ e.preventDefault();
1044
+ const formData = new FormData(e.currentTarget);
1045
+ const searchQuery = formData.get(name);
1046
+ updateSearchParams(String(searchQuery ?? ""));
1047
+ };
1048
+ const handleInputChange = (e) => {
1049
+ const value = e.target.value;
1050
+ setInputValue(value);
1051
+ if (liveSearch) {
1052
+ if (debounceTimerRef.current) {
1053
+ clearTimeout(debounceTimerRef.current);
1054
+ }
1055
+ debounceTimerRef.current = setTimeout(() => {
1056
+ updateSearchParams(value);
1057
+ }, debounceDelay);
1058
+ }
1059
+ if (value === "") {
1060
+ if (debounceTimerRef.current) {
1061
+ clearTimeout(debounceTimerRef.current);
1062
+ }
1063
+ setInputValue("");
1064
+ updateSearchParams("");
1065
+ }
1066
+ };
1067
+ (0, import_react4.useEffect)(() => {
1068
+ setInputValue(queryParam);
1069
+ }, [queryParam]);
1070
+ (0, import_react4.useEffect)(() => {
1071
+ return () => {
1072
+ if (debounceTimerRef.current) {
1073
+ clearTimeout(debounceTimerRef.current);
1074
+ }
1075
+ };
1076
+ }, []);
1077
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1078
+ "form",
1079
+ {
1080
+ onSubmit: handleSearch,
1081
+ className: cn(
1082
+ "flex items-center justify-between bg-white rounded-xl px-2 h-11 border",
1083
+ searchWrapperClass
1084
+ ),
1085
+ children: [
1086
+ showIcon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1087
+ "button",
1088
+ {
1089
+ type: "submit",
1090
+ className: cn(
1091
+ "bg-white text-secondary rounded-full flex items-center justify-center w-10 h-10",
1092
+ searchBtn
1093
+ ),
1094
+ children: customIcon ? customIcon : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1095
+ import_fi.FiSearch,
1096
+ {
1097
+ className: cn("h-5 w-5 text-gray-400", searchIconClassName)
1098
+ }
1099
+ )
1100
+ }
1101
+ ),
1102
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1103
+ "input",
1104
+ {
1105
+ value: inputValue,
1106
+ onChange: handleInputChange,
1107
+ name,
1108
+ type,
1109
+ className: cn("outline-none text-sm w-96", searchClassName),
1110
+ placeholder,
1111
+ ...props
1112
+ }
1113
+ )
1114
+ ]
1115
+ }
1116
+ );
1117
+ }
1118
+
1002
1119
  // src/components/modal/Modal.tsx
1003
1120
  var import_framer_motion = require("framer-motion");
1004
- var import_react4 = require("react");
1121
+ var import_react5 = require("react");
1005
1122
  var import_react_dom = require("react-dom");
1006
1123
  var import_fa = require("react-icons/fa");
1007
1124
  var import_io5 = require("react-icons/io5");
1008
1125
 
1009
1126
  // src/components/Loader/Loader.tsx
1010
- var import_jsx_runtime4 = require("react/jsx-runtime");
1127
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1011
1128
  var Loader = ({ className, color = "#F54C00", size = "50px", speed = "1s" }) => {
1012
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `loader`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1129
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `loader`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1013
1130
  "div",
1014
1131
  {
1015
1132
  className: `spinner ${className}`,
@@ -1026,7 +1143,7 @@ var Loader = ({ className, color = "#F54C00", size = "50px", speed = "1s" }) =>
1026
1143
  };
1027
1144
 
1028
1145
  // src/components/modal/Modal.tsx
1029
- var import_jsx_runtime5 = require("react/jsx-runtime");
1146
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1030
1147
  function Modal({
1031
1148
  isOpen,
1032
1149
  onClose,
@@ -1046,13 +1163,13 @@ function Modal({
1046
1163
  customLoader = null,
1047
1164
  footerClassName
1048
1165
  }) {
1049
- const modalRef = (0, import_react4.useRef)(null);
1166
+ const modalRef = (0, import_react5.useRef)(null);
1050
1167
  const handleBackdropClick = (e) => {
1051
1168
  if (e.target === e.currentTarget && closeOnBackdropClick) {
1052
1169
  onClose();
1053
1170
  }
1054
1171
  };
1055
- (0, import_react4.useEffect)(() => {
1172
+ (0, import_react5.useEffect)(() => {
1056
1173
  const handleEscape = (e) => {
1057
1174
  if (e.key === "Escape" && isOpen && closeOnEscape) {
1058
1175
  onClose();
@@ -1067,7 +1184,7 @@ function Modal({
1067
1184
  document.body.style.overflow = "unset";
1068
1185
  };
1069
1186
  }, [isOpen, onClose, closeOnEscape]);
1070
- const modalContent = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_framer_motion.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1187
+ const modalContent = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_framer_motion.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1071
1188
  "div",
1072
1189
  {
1073
1190
  className: "fixed inset-0 bg-gray-500 bg-opacity-50 flex justify-center items-center z-1000 p-4",
@@ -1086,7 +1203,7 @@ function Modal({
1086
1203
  zIndex
1087
1204
  },
1088
1205
  children: [
1089
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1206
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1090
1207
  import_framer_motion.motion.div,
1091
1208
  {
1092
1209
  ref: modalRef,
@@ -1107,7 +1224,7 @@ function Modal({
1107
1224
  // Ensure modal doesn't exceed viewport height
1108
1225
  },
1109
1226
  children: [
1110
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1227
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1111
1228
  "div",
1112
1229
  {
1113
1230
  style: {
@@ -1115,43 +1232,43 @@ function Modal({
1115
1232
  },
1116
1233
  className: "sticky top-0 z-10 flex justify-between items-center bg-white text-2xl p-8 pb-4 rounded-t-3xl",
1117
1234
  children: [
1118
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "font-semibold", children: title }),
1119
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center space-x-4", children: [
1120
- onPrint && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1235
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "font-semibold", children: title }),
1236
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center space-x-4", children: [
1237
+ onPrint && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1121
1238
  "button",
1122
1239
  {
1123
1240
  onClick: onPrint,
1124
1241
  className: "flex items-center gap-1 hover:text-gray-300 text-sm border p-1 rounded",
1125
1242
  children: [
1126
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_fa.FaPrint, { name: "FaPrint" }),
1127
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Print" })
1243
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_fa.FaPrint, { name: "FaPrint" }),
1244
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Print" })
1128
1245
  ]
1129
1246
  }
1130
1247
  ),
1131
- onExport && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1248
+ onExport && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1132
1249
  "button",
1133
1250
  {
1134
1251
  onClick: onExport,
1135
1252
  className: "flex items-center gap-1 hover:text-gray-300 text-sm border p-1 rounded",
1136
1253
  children: [
1137
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_fa.FaFileExport, {}),
1138
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Export" })
1254
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_fa.FaFileExport, {}),
1255
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Export" })
1139
1256
  ]
1140
1257
  }
1141
1258
  ),
1142
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1259
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1143
1260
  "button",
1144
1261
  {
1145
1262
  onClick: onClose,
1146
1263
  className: "px-2 hover:opacity-70 transition-opacity",
1147
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_io5.IoCloseCircleOutline, { color: "#8898AA", size: 25 })
1264
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_io5.IoCloseCircleOutline, { color: "#8898AA", size: 25 })
1148
1265
  }
1149
1266
  )
1150
1267
  ] })
1151
1268
  ]
1152
1269
  }
1153
1270
  ),
1154
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1271
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1155
1272
  "div",
1156
1273
  {
1157
1274
  className: cn(
@@ -1161,7 +1278,7 @@ function Modal({
1161
1278
  children
1162
1279
  }
1163
1280
  ),
1164
- footer && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1281
+ footer && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1165
1282
  "div",
1166
1283
  {
1167
1284
  className: cn("sticky bottom-0 flex justify-end p-4 space-x-4 border-t bg-white ", footerClassName),
@@ -1175,7 +1292,7 @@ function Modal({
1175
1292
  ]
1176
1293
  }
1177
1294
  ),
1178
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1295
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1179
1296
  "div",
1180
1297
  {
1181
1298
  className: "fixed inset-0 flex items-center justify-center bg-gray-200/60",
@@ -1184,7 +1301,7 @@ function Modal({
1184
1301
  "aria-live": "polite",
1185
1302
  role: "status",
1186
1303
  onClick: (e) => e.stopPropagation(),
1187
- children: customLoader ? customLoader : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Loader, { size: "48px" })
1304
+ children: customLoader ? customLoader : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Loader, { size: "48px" })
1188
1305
  }
1189
1306
  )
1190
1307
  ]
@@ -1198,14 +1315,220 @@ function Modal({
1198
1315
  }
1199
1316
  return (0, import_react_dom.createPortal)(modalContent, modalRoot);
1200
1317
  }
1318
+
1319
+ // src/components/cell-editor/CustomCellEditor.tsx
1320
+ var import_react6 = require("react");
1321
+ var import_react_hot_toast2 = __toESM(require("react-hot-toast"));
1322
+ var import_fa2 = require("react-icons/fa");
1323
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1324
+ function ButtonSpinner() {
1325
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1326
+ "svg",
1327
+ {
1328
+ className: "animate-spin",
1329
+ width: 8,
1330
+ height: 8,
1331
+ viewBox: "0 0 24 24",
1332
+ fill: "none",
1333
+ "aria-hidden": true,
1334
+ children: [
1335
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1336
+ "circle",
1337
+ {
1338
+ cx: "12",
1339
+ cy: "12",
1340
+ r: "10",
1341
+ stroke: "white",
1342
+ strokeWidth: "4",
1343
+ strokeOpacity: "0.25"
1344
+ }
1345
+ ),
1346
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1347
+ "path",
1348
+ {
1349
+ d: "M12 2a10 10 0 0 1 10 10",
1350
+ stroke: "white",
1351
+ strokeWidth: "4",
1352
+ strokeLinecap: "round"
1353
+ }
1354
+ )
1355
+ ]
1356
+ }
1357
+ );
1358
+ }
1359
+ function normalizeInitialValue(initialValue, inputType) {
1360
+ if (inputType === "date" && initialValue != null && initialValue !== "") {
1361
+ return toDateInputValue(String(initialValue));
1362
+ }
1363
+ if (initialValue == null) {
1364
+ return "";
1365
+ }
1366
+ return initialValue;
1367
+ }
1368
+ var CustomCellEditor = (0, import_react6.forwardRef)(
1369
+ ({
1370
+ value: initialValue,
1371
+ name,
1372
+ inputType = "text",
1373
+ stopEditing,
1374
+ entityId,
1375
+ entityName = "Record",
1376
+ onSave,
1377
+ dropdownOptions = []
1378
+ }, ref) => {
1379
+ const normalizedInitial = normalizeInitialValue(initialValue, inputType);
1380
+ const [value, setValue] = (0, import_react6.useState)(normalizedInitial);
1381
+ const [isLoading, setIsLoading] = (0, import_react6.useState)(false);
1382
+ const inputRef = (0, import_react6.useRef)(null);
1383
+ const selectRef = (0, import_react6.useRef)(null);
1384
+ (0, import_react6.useImperativeHandle)(ref, () => ({
1385
+ getValue: () => value,
1386
+ isCancelBeforeStart: () => false,
1387
+ isCancelAfterEnd: () => false
1388
+ }));
1389
+ (0, import_react6.useEffect)(() => {
1390
+ if (inputType === "dropdown") {
1391
+ const el2 = selectRef.current;
1392
+ if (el2) {
1393
+ el2.focus();
1394
+ }
1395
+ return;
1396
+ }
1397
+ const el = inputRef.current;
1398
+ if (!el) {
1399
+ return;
1400
+ }
1401
+ el.focus();
1402
+ if (inputType !== "date") {
1403
+ el.select();
1404
+ }
1405
+ }, [inputType]);
1406
+ const handleSave = async () => {
1407
+ if (isLoading) {
1408
+ return;
1409
+ }
1410
+ if (String(value) === String(normalizedInitial)) {
1411
+ stopEditing();
1412
+ return;
1413
+ }
1414
+ if (!entityId) {
1415
+ import_react_hot_toast2.default.error("Entity ID is required");
1416
+ return;
1417
+ }
1418
+ if (!name) {
1419
+ import_react_hot_toast2.default.error("Property name is required");
1420
+ return;
1421
+ }
1422
+ setIsLoading(true);
1423
+ const payloadValue = inputType === "number" && value !== "" ? parseFloat(String(value)) : value;
1424
+ const payload = {
1425
+ propertyName: name,
1426
+ propertyValue: payloadValue,
1427
+ entityId,
1428
+ entityName
1429
+ };
1430
+ try {
1431
+ await onSave(payload);
1432
+ import_react_hot_toast2.default.success("Record updated successfully");
1433
+ stopEditing();
1434
+ } catch (error) {
1435
+ console.error("Update error:", error);
1436
+ const errorMessage = error instanceof Error ? error.message : "Failed to update record";
1437
+ import_react_hot_toast2.default.error(errorMessage);
1438
+ } finally {
1439
+ setIsLoading(false);
1440
+ }
1441
+ };
1442
+ const handleCancel = () => {
1443
+ setValue(normalizedInitial);
1444
+ stopEditing();
1445
+ };
1446
+ const handleKeyDown = (event) => {
1447
+ if (event.key === "Enter") {
1448
+ event.preventDefault();
1449
+ handleSave();
1450
+ } else if (event.key === "Escape") {
1451
+ event.preventDefault();
1452
+ handleCancel();
1453
+ }
1454
+ };
1455
+ const handleInputChange = (event) => {
1456
+ let newValue = event.target.value;
1457
+ if (inputType === "number") {
1458
+ newValue = newValue.replace(/[^0-9.-]/g, "");
1459
+ }
1460
+ setValue(newValue);
1461
+ };
1462
+ const handleDropdownChange = (event) => {
1463
+ setValue(event.target.value);
1464
+ };
1465
+ const isSaveDisabled = isLoading || String(value) === String(normalizedInitial);
1466
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center w-full h-full min-h-0 overflow-hidden gap-0.5 min-w-[80px]", children: [
1467
+ inputType === "dropdown" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1468
+ "select",
1469
+ {
1470
+ ref: selectRef,
1471
+ value: String(value),
1472
+ onChange: handleDropdownChange,
1473
+ onKeyDown: handleKeyDown,
1474
+ className: "flex-1 h-full p-0 text-[10px] border border-gray-300 rounded-sm focus:outline-none focus:ring-1 focus:ring-blue-400 min-w-0 truncate",
1475
+ disabled: isLoading,
1476
+ children: [
1477
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", disabled: true, children: "Select option..." }),
1478
+ dropdownOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
1479
+ ]
1480
+ }
1481
+ ) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1482
+ "input",
1483
+ {
1484
+ ref: inputRef,
1485
+ type: inputType,
1486
+ value: String(value),
1487
+ onChange: handleInputChange,
1488
+ onKeyDown: handleKeyDown,
1489
+ className: "flex-1 h-full p-0 text-[10px] border border-gray-300 rounded-sm focus:outline-none focus:ring-1 focus:ring-blue-400 min-w-0 truncate",
1490
+ placeholder: "...",
1491
+ disabled: isLoading
1492
+ }
1493
+ ),
1494
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center shrink-0 gap-0.5", children: [
1495
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1496
+ "button",
1497
+ {
1498
+ type: "button",
1499
+ onClick: handleSave,
1500
+ disabled: isSaveDisabled,
1501
+ className: "flex items-center justify-center w-4 h-4 text-white bg-green-500 rounded-sm hover:bg-green-600 disabled:opacity-50",
1502
+ title: "Save (Enter)",
1503
+ children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ButtonSpinner, {}) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_fa2.FaCheck, { size: 7, "aria-hidden": true })
1504
+ }
1505
+ ),
1506
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1507
+ "button",
1508
+ {
1509
+ type: "button",
1510
+ onClick: handleCancel,
1511
+ disabled: isLoading,
1512
+ className: "flex items-center justify-center w-4 h-4 text-white bg-red-500 rounded-sm hover:bg-red-600 disabled:opacity-50",
1513
+ title: "Cancel (Escape)",
1514
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_fa2.FaTimes, { size: 7, "aria-hidden": true })
1515
+ }
1516
+ )
1517
+ ] })
1518
+ ] });
1519
+ }
1520
+ );
1521
+ CustomCellEditor.displayName = "CustomCellEditor";
1201
1522
  // Annotate the CommonJS export names for ESM import in node:
1202
1523
  0 && (module.exports = {
1524
+ CustomCellEditor,
1203
1525
  CustomPagination,
1204
1526
  EztrakTabs,
1205
1527
  Loader,
1206
1528
  Modal,
1207
1529
  ResetColumnsButton,
1208
1530
  ResetFiltersButton,
1531
+ SearchInput,
1209
1532
  TableLayoutToolbarControls,
1210
1533
  capitalizeText,
1211
1534
  checkIfExistsInArray,