@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.
@@ -30,12 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/components/index.ts
31
31
  var components_exports = {};
32
32
  __export(components_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
  });
41
43
  module.exports = __toCommonJS(components_exports);
@@ -661,17 +663,132 @@ function ResetFiltersButton({
661
663
  );
662
664
  }
663
665
 
666
+ // src/components/search-filters/SearchInput.tsx
667
+ var import_react2 = require("react");
668
+ var import_fi = require("react-icons/fi");
669
+ var import_react_router_dom3 = require("react-router-dom");
670
+ var import_jsx_runtime4 = require("react/jsx-runtime");
671
+ function SearchInput({
672
+ placeholder = "Search...",
673
+ searchBtn = "",
674
+ searchClassName = "",
675
+ searchWrapperClass = "",
676
+ searchIconClassName = "",
677
+ defaultParam = "query",
678
+ showIcon = true,
679
+ name = "search",
680
+ type = "search",
681
+ liveSearch = false,
682
+ debounceDelay = 500,
683
+ customIcon = null,
684
+ defaultValue,
685
+ ...props
686
+ }) {
687
+ const [searchParams, setSearchParams] = (0, import_react_router_dom3.useSearchParams)();
688
+ const queryParam = searchParams.get(defaultParam) || defaultValue || "";
689
+ const [inputValue, setInputValue] = (0, import_react2.useState)(queryParam);
690
+ const debounceTimerRef = (0, import_react2.useRef)(null);
691
+ const updateSearchParams = (searchQuery) => {
692
+ if (searchQuery?.trim()) {
693
+ setSearchParams((prev) => {
694
+ prev.set(defaultParam, searchQuery.trim());
695
+ return prev;
696
+ });
697
+ } else {
698
+ setSearchParams((prev) => {
699
+ prev.delete(defaultParam);
700
+ return prev;
701
+ });
702
+ }
703
+ };
704
+ const handleSearch = (e) => {
705
+ e.preventDefault();
706
+ const formData = new FormData(e.currentTarget);
707
+ const searchQuery = formData.get(name);
708
+ updateSearchParams(String(searchQuery ?? ""));
709
+ };
710
+ const handleInputChange = (e) => {
711
+ const value = e.target.value;
712
+ setInputValue(value);
713
+ if (liveSearch) {
714
+ if (debounceTimerRef.current) {
715
+ clearTimeout(debounceTimerRef.current);
716
+ }
717
+ debounceTimerRef.current = setTimeout(() => {
718
+ updateSearchParams(value);
719
+ }, debounceDelay);
720
+ }
721
+ if (value === "") {
722
+ if (debounceTimerRef.current) {
723
+ clearTimeout(debounceTimerRef.current);
724
+ }
725
+ setInputValue("");
726
+ updateSearchParams("");
727
+ }
728
+ };
729
+ (0, import_react2.useEffect)(() => {
730
+ setInputValue(queryParam);
731
+ }, [queryParam]);
732
+ (0, import_react2.useEffect)(() => {
733
+ return () => {
734
+ if (debounceTimerRef.current) {
735
+ clearTimeout(debounceTimerRef.current);
736
+ }
737
+ };
738
+ }, []);
739
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
740
+ "form",
741
+ {
742
+ onSubmit: handleSearch,
743
+ className: cn(
744
+ "flex items-center justify-between bg-white rounded-xl px-2 h-11 border",
745
+ searchWrapperClass
746
+ ),
747
+ children: [
748
+ showIcon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
749
+ "button",
750
+ {
751
+ type: "submit",
752
+ className: cn(
753
+ "bg-white text-secondary rounded-full flex items-center justify-center w-10 h-10",
754
+ searchBtn
755
+ ),
756
+ children: customIcon ? customIcon : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
757
+ import_fi.FiSearch,
758
+ {
759
+ className: cn("h-5 w-5 text-gray-400", searchIconClassName)
760
+ }
761
+ )
762
+ }
763
+ ),
764
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
765
+ "input",
766
+ {
767
+ value: inputValue,
768
+ onChange: handleInputChange,
769
+ name,
770
+ type,
771
+ className: cn("outline-none text-sm w-96", searchClassName),
772
+ placeholder,
773
+ ...props
774
+ }
775
+ )
776
+ ]
777
+ }
778
+ );
779
+ }
780
+
664
781
  // src/components/modal/Modal.tsx
665
782
  var import_framer_motion = require("framer-motion");
666
- var import_react2 = require("react");
783
+ var import_react3 = require("react");
667
784
  var import_react_dom = require("react-dom");
668
785
  var import_fa = require("react-icons/fa");
669
786
  var import_io5 = require("react-icons/io5");
670
787
 
671
788
  // src/components/Loader/Loader.tsx
672
- var import_jsx_runtime4 = require("react/jsx-runtime");
789
+ var import_jsx_runtime5 = require("react/jsx-runtime");
673
790
  var Loader = ({ className, color = "#F54C00", size = "50px", speed = "1s" }) => {
674
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `loader`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
791
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `loader`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
675
792
  "div",
676
793
  {
677
794
  className: `spinner ${className}`,
@@ -688,7 +805,7 @@ var Loader = ({ className, color = "#F54C00", size = "50px", speed = "1s" }) =>
688
805
  };
689
806
 
690
807
  // src/components/modal/Modal.tsx
691
- var import_jsx_runtime5 = require("react/jsx-runtime");
808
+ var import_jsx_runtime6 = require("react/jsx-runtime");
692
809
  function Modal({
693
810
  isOpen,
694
811
  onClose,
@@ -708,13 +825,13 @@ function Modal({
708
825
  customLoader = null,
709
826
  footerClassName
710
827
  }) {
711
- const modalRef = (0, import_react2.useRef)(null);
828
+ const modalRef = (0, import_react3.useRef)(null);
712
829
  const handleBackdropClick = (e) => {
713
830
  if (e.target === e.currentTarget && closeOnBackdropClick) {
714
831
  onClose();
715
832
  }
716
833
  };
717
- (0, import_react2.useEffect)(() => {
834
+ (0, import_react3.useEffect)(() => {
718
835
  const handleEscape = (e) => {
719
836
  if (e.key === "Escape" && isOpen && closeOnEscape) {
720
837
  onClose();
@@ -729,7 +846,7 @@ function Modal({
729
846
  document.body.style.overflow = "unset";
730
847
  };
731
848
  }, [isOpen, onClose, closeOnEscape]);
732
- const modalContent = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_framer_motion.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
849
+ const modalContent = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_framer_motion.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
733
850
  "div",
734
851
  {
735
852
  className: "fixed inset-0 bg-gray-500 bg-opacity-50 flex justify-center items-center z-1000 p-4",
@@ -748,7 +865,7 @@ function Modal({
748
865
  zIndex
749
866
  },
750
867
  children: [
751
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
868
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
752
869
  import_framer_motion.motion.div,
753
870
  {
754
871
  ref: modalRef,
@@ -769,7 +886,7 @@ function Modal({
769
886
  // Ensure modal doesn't exceed viewport height
770
887
  },
771
888
  children: [
772
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
889
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
773
890
  "div",
774
891
  {
775
892
  style: {
@@ -777,43 +894,43 @@ function Modal({
777
894
  },
778
895
  className: "sticky top-0 z-10 flex justify-between items-center bg-white text-2xl p-8 pb-4 rounded-t-3xl",
779
896
  children: [
780
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "font-semibold", children: title }),
781
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center space-x-4", children: [
782
- onPrint && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
897
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "font-semibold", children: title }),
898
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center space-x-4", children: [
899
+ onPrint && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
783
900
  "button",
784
901
  {
785
902
  onClick: onPrint,
786
903
  className: "flex items-center gap-1 hover:text-gray-300 text-sm border p-1 rounded",
787
904
  children: [
788
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_fa.FaPrint, { name: "FaPrint" }),
789
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Print" })
905
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_fa.FaPrint, { name: "FaPrint" }),
906
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Print" })
790
907
  ]
791
908
  }
792
909
  ),
793
- onExport && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
910
+ onExport && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
794
911
  "button",
795
912
  {
796
913
  onClick: onExport,
797
914
  className: "flex items-center gap-1 hover:text-gray-300 text-sm border p-1 rounded",
798
915
  children: [
799
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_fa.FaFileExport, {}),
800
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Export" })
916
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_fa.FaFileExport, {}),
917
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Export" })
801
918
  ]
802
919
  }
803
920
  ),
804
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
921
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
805
922
  "button",
806
923
  {
807
924
  onClick: onClose,
808
925
  className: "px-2 hover:opacity-70 transition-opacity",
809
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_io5.IoCloseCircleOutline, { color: "#8898AA", size: 25 })
926
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_io5.IoCloseCircleOutline, { color: "#8898AA", size: 25 })
810
927
  }
811
928
  )
812
929
  ] })
813
930
  ]
814
931
  }
815
932
  ),
816
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
933
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
817
934
  "div",
818
935
  {
819
936
  className: cn(
@@ -823,7 +940,7 @@ function Modal({
823
940
  children
824
941
  }
825
942
  ),
826
- footer && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
943
+ footer && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
827
944
  "div",
828
945
  {
829
946
  className: cn("sticky bottom-0 flex justify-end p-4 space-x-4 border-t bg-white ", footerClassName),
@@ -837,7 +954,7 @@ function Modal({
837
954
  ]
838
955
  }
839
956
  ),
840
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
957
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
841
958
  "div",
842
959
  {
843
960
  className: "fixed inset-0 flex items-center justify-center bg-gray-200/60",
@@ -846,7 +963,7 @@ function Modal({
846
963
  "aria-live": "polite",
847
964
  role: "status",
848
965
  onClick: (e) => e.stopPropagation(),
849
- children: customLoader ? customLoader : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Loader, { size: "48px" })
966
+ children: customLoader ? customLoader : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Loader, { size: "48px" })
850
967
  }
851
968
  )
852
969
  ]
@@ -860,14 +977,242 @@ function Modal({
860
977
  }
861
978
  return (0, import_react_dom.createPortal)(modalContent, modalRoot);
862
979
  }
980
+
981
+ // src/components/cell-editor/CustomCellEditor.tsx
982
+ var import_react4 = require("react");
983
+ var import_react_hot_toast2 = __toESM(require("react-hot-toast"));
984
+ var import_fa2 = require("react-icons/fa");
985
+
986
+ // src/utils/helpers.ts
987
+ var toDateInputValue = (date) => {
988
+ if (!date) return "";
989
+ let d;
990
+ if (typeof date === "string") {
991
+ if (/^\d{4}-\d{2}-\d{2}$/.test(date)) {
992
+ return date;
993
+ }
994
+ d = new Date(date);
995
+ } else {
996
+ d = new Date(date);
997
+ }
998
+ if (isNaN(d.getTime())) return "";
999
+ const localDate = new Date(d.getTime() - d.getTimezoneOffset() * 6e4);
1000
+ const year = localDate.getFullYear();
1001
+ const month = (localDate.getMonth() + 1).toString().padStart(2, "0");
1002
+ const day = localDate.getDate().toString().padStart(2, "0");
1003
+ return `${year}-${month}-${day}`;
1004
+ };
1005
+
1006
+ // src/components/cell-editor/CustomCellEditor.tsx
1007
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1008
+ function ButtonSpinner() {
1009
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1010
+ "svg",
1011
+ {
1012
+ className: "animate-spin",
1013
+ width: 8,
1014
+ height: 8,
1015
+ viewBox: "0 0 24 24",
1016
+ fill: "none",
1017
+ "aria-hidden": true,
1018
+ children: [
1019
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1020
+ "circle",
1021
+ {
1022
+ cx: "12",
1023
+ cy: "12",
1024
+ r: "10",
1025
+ stroke: "white",
1026
+ strokeWidth: "4",
1027
+ strokeOpacity: "0.25"
1028
+ }
1029
+ ),
1030
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1031
+ "path",
1032
+ {
1033
+ d: "M12 2a10 10 0 0 1 10 10",
1034
+ stroke: "white",
1035
+ strokeWidth: "4",
1036
+ strokeLinecap: "round"
1037
+ }
1038
+ )
1039
+ ]
1040
+ }
1041
+ );
1042
+ }
1043
+ function normalizeInitialValue(initialValue, inputType) {
1044
+ if (inputType === "date" && initialValue != null && initialValue !== "") {
1045
+ return toDateInputValue(String(initialValue));
1046
+ }
1047
+ if (initialValue == null) {
1048
+ return "";
1049
+ }
1050
+ return initialValue;
1051
+ }
1052
+ var CustomCellEditor = (0, import_react4.forwardRef)(
1053
+ ({
1054
+ value: initialValue,
1055
+ name,
1056
+ inputType = "text",
1057
+ stopEditing,
1058
+ entityId,
1059
+ entityName = "Record",
1060
+ onSave,
1061
+ dropdownOptions = []
1062
+ }, ref) => {
1063
+ const normalizedInitial = normalizeInitialValue(initialValue, inputType);
1064
+ const [value, setValue] = (0, import_react4.useState)(normalizedInitial);
1065
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
1066
+ const inputRef = (0, import_react4.useRef)(null);
1067
+ const selectRef = (0, import_react4.useRef)(null);
1068
+ (0, import_react4.useImperativeHandle)(ref, () => ({
1069
+ getValue: () => value,
1070
+ isCancelBeforeStart: () => false,
1071
+ isCancelAfterEnd: () => false
1072
+ }));
1073
+ (0, import_react4.useEffect)(() => {
1074
+ if (inputType === "dropdown") {
1075
+ const el2 = selectRef.current;
1076
+ if (el2) {
1077
+ el2.focus();
1078
+ }
1079
+ return;
1080
+ }
1081
+ const el = inputRef.current;
1082
+ if (!el) {
1083
+ return;
1084
+ }
1085
+ el.focus();
1086
+ if (inputType !== "date") {
1087
+ el.select();
1088
+ }
1089
+ }, [inputType]);
1090
+ const handleSave = async () => {
1091
+ if (isLoading) {
1092
+ return;
1093
+ }
1094
+ if (String(value) === String(normalizedInitial)) {
1095
+ stopEditing();
1096
+ return;
1097
+ }
1098
+ if (!entityId) {
1099
+ import_react_hot_toast2.default.error("Entity ID is required");
1100
+ return;
1101
+ }
1102
+ if (!name) {
1103
+ import_react_hot_toast2.default.error("Property name is required");
1104
+ return;
1105
+ }
1106
+ setIsLoading(true);
1107
+ const payloadValue = inputType === "number" && value !== "" ? parseFloat(String(value)) : value;
1108
+ const payload = {
1109
+ propertyName: name,
1110
+ propertyValue: payloadValue,
1111
+ entityId,
1112
+ entityName
1113
+ };
1114
+ try {
1115
+ await onSave(payload);
1116
+ import_react_hot_toast2.default.success("Record updated successfully");
1117
+ stopEditing();
1118
+ } catch (error) {
1119
+ console.error("Update error:", error);
1120
+ const errorMessage = error instanceof Error ? error.message : "Failed to update record";
1121
+ import_react_hot_toast2.default.error(errorMessage);
1122
+ } finally {
1123
+ setIsLoading(false);
1124
+ }
1125
+ };
1126
+ const handleCancel = () => {
1127
+ setValue(normalizedInitial);
1128
+ stopEditing();
1129
+ };
1130
+ const handleKeyDown = (event) => {
1131
+ if (event.key === "Enter") {
1132
+ event.preventDefault();
1133
+ handleSave();
1134
+ } else if (event.key === "Escape") {
1135
+ event.preventDefault();
1136
+ handleCancel();
1137
+ }
1138
+ };
1139
+ const handleInputChange = (event) => {
1140
+ let newValue = event.target.value;
1141
+ if (inputType === "number") {
1142
+ newValue = newValue.replace(/[^0-9.-]/g, "");
1143
+ }
1144
+ setValue(newValue);
1145
+ };
1146
+ const handleDropdownChange = (event) => {
1147
+ setValue(event.target.value);
1148
+ };
1149
+ const isSaveDisabled = isLoading || String(value) === String(normalizedInitial);
1150
+ 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: [
1151
+ inputType === "dropdown" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1152
+ "select",
1153
+ {
1154
+ ref: selectRef,
1155
+ value: String(value),
1156
+ onChange: handleDropdownChange,
1157
+ onKeyDown: handleKeyDown,
1158
+ 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",
1159
+ disabled: isLoading,
1160
+ children: [
1161
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", disabled: true, children: "Select option..." }),
1162
+ dropdownOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
1163
+ ]
1164
+ }
1165
+ ) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1166
+ "input",
1167
+ {
1168
+ ref: inputRef,
1169
+ type: inputType,
1170
+ value: String(value),
1171
+ onChange: handleInputChange,
1172
+ onKeyDown: handleKeyDown,
1173
+ 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",
1174
+ placeholder: "...",
1175
+ disabled: isLoading
1176
+ }
1177
+ ),
1178
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center shrink-0 gap-0.5", children: [
1179
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1180
+ "button",
1181
+ {
1182
+ type: "button",
1183
+ onClick: handleSave,
1184
+ disabled: isSaveDisabled,
1185
+ className: "flex items-center justify-center w-4 h-4 text-white bg-green-500 rounded-sm hover:bg-green-600 disabled:opacity-50",
1186
+ title: "Save (Enter)",
1187
+ children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ButtonSpinner, {}) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_fa2.FaCheck, { size: 7, "aria-hidden": true })
1188
+ }
1189
+ ),
1190
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1191
+ "button",
1192
+ {
1193
+ type: "button",
1194
+ onClick: handleCancel,
1195
+ disabled: isLoading,
1196
+ className: "flex items-center justify-center w-4 h-4 text-white bg-red-500 rounded-sm hover:bg-red-600 disabled:opacity-50",
1197
+ title: "Cancel (Escape)",
1198
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_fa2.FaTimes, { size: 7, "aria-hidden": true })
1199
+ }
1200
+ )
1201
+ ] })
1202
+ ] });
1203
+ }
1204
+ );
1205
+ CustomCellEditor.displayName = "CustomCellEditor";
863
1206
  // Annotate the CommonJS export names for ESM import in node:
864
1207
  0 && (module.exports = {
1208
+ CustomCellEditor,
865
1209
  CustomPagination,
866
1210
  EztrakTabs,
867
1211
  Loader,
868
1212
  Modal,
869
1213
  ResetColumnsButton,
870
1214
  ResetFiltersButton,
1215
+ SearchInput,
871
1216
  TableLayoutToolbarControls
872
1217
  });
873
1218
  //# sourceMappingURL=index.js.map