@solidxai/core-ui 0.1.13-beta.4 → 0.1.13-beta.6

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.
Files changed (149) hide show
  1. package/dist/components/auth/AuthTabs.d.ts.map +1 -1
  2. package/dist/components/auth/AuthTabs.js +1 -1
  3. package/dist/components/auth/AuthTabs.js.map +1 -1
  4. package/dist/components/auth/AuthTabs.tsx +1 -0
  5. package/dist/components/auth/SolidChangeForcePassword.d.ts.map +1 -1
  6. package/dist/components/auth/SolidChangeForcePassword.js +2 -2
  7. package/dist/components/auth/SolidChangeForcePassword.js.map +1 -1
  8. package/dist/components/auth/SolidChangeForcePassword.tsx +9 -8
  9. package/dist/components/common/SettingsComponent.d.ts.map +1 -1
  10. package/dist/components/common/SettingsComponent.js +1 -1
  11. package/dist/components/common/SettingsComponent.js.map +1 -1
  12. package/dist/components/common/SettingsComponent.tsx +1 -0
  13. package/dist/components/common/SolidFieldTooltip.d.ts.map +1 -1
  14. package/dist/components/common/SolidFieldTooltip.js +1 -33
  15. package/dist/components/common/SolidFieldTooltip.js.map +1 -1
  16. package/dist/components/common/SolidFieldTooltip.tsx +8 -64
  17. package/dist/components/common/SolidMaterialSymbol.d.ts +1 -1
  18. package/dist/components/common/SolidMaterialSymbol.d.ts.map +1 -1
  19. package/dist/components/common/SolidMaterialSymbol.js +6 -37
  20. package/dist/components/common/SolidMaterialSymbol.js.map +1 -1
  21. package/dist/components/common/SolidMaterialSymbol.tsx +6 -54
  22. package/dist/components/common/SolidSettings/LlmSettings/AiModelConfigTab.d.ts.map +1 -1
  23. package/dist/components/common/SolidSettings/LlmSettings/AiModelConfigTab.js +2 -2
  24. package/dist/components/common/SolidSettings/LlmSettings/AiModelConfigTab.js.map +1 -1
  25. package/dist/components/common/SolidSettings/LlmSettings/AiModelConfigTab.tsx +2 -0
  26. package/dist/components/core/card/CardItem.d.ts.map +1 -1
  27. package/dist/components/core/card/CardItem.js +1 -1
  28. package/dist/components/core/card/CardItem.js.map +1 -1
  29. package/dist/components/core/card/CardItem.tsx +16 -14
  30. package/dist/components/core/card/SolidCardView.d.ts.map +1 -1
  31. package/dist/components/core/card/SolidCardView.js +69 -40
  32. package/dist/components/core/card/SolidCardView.js.map +1 -1
  33. package/dist/components/core/card/SolidCardView.tsx +55 -24
  34. package/dist/components/core/common/SolidAccountSettings/SolidAccountSettings.module.css +99 -0
  35. package/dist/components/core/common/SolidAccountSettings/SolidVersionInfo.d.ts.map +1 -1
  36. package/dist/components/core/common/SolidAccountSettings/SolidVersionInfo.js +76 -5
  37. package/dist/components/core/common/SolidAccountSettings/SolidVersionInfo.js.map +1 -1
  38. package/dist/components/core/common/SolidAccountSettings/SolidVersionInfo.tsx +39 -4
  39. package/dist/components/core/common/SolidGlobalSearchElement.d.ts.map +1 -1
  40. package/dist/components/core/common/SolidGlobalSearchElement.js +172 -124
  41. package/dist/components/core/common/SolidGlobalSearchElement.js.map +1 -1
  42. package/dist/components/core/common/SolidGlobalSearchElement.tsx +73 -84
  43. package/dist/components/core/common/globalSearchPersistence.d.ts +3 -0
  44. package/dist/components/core/common/globalSearchPersistence.d.ts.map +1 -1
  45. package/dist/components/core/common/globalSearchPersistence.js +33 -24
  46. package/dist/components/core/common/globalSearchPersistence.js.map +1 -1
  47. package/dist/components/core/common/globalSearchPersistence.ts +34 -27
  48. package/dist/components/core/filter/fields/SolidMediaMultipleField.js +2 -2
  49. package/dist/components/core/filter/fields/SolidMediaMultipleField.js.map +1 -1
  50. package/dist/components/core/filter/fields/SolidMediaMultipleField.tsx +2 -2
  51. package/dist/components/core/filter/fields/SolidMediaSingleField.js +2 -2
  52. package/dist/components/core/filter/fields/SolidMediaSingleField.js.map +1 -1
  53. package/dist/components/core/filter/fields/SolidMediaSingleField.tsx +2 -2
  54. package/dist/components/core/filter/fields/SolidShortTextField.js +2 -2
  55. package/dist/components/core/filter/fields/SolidShortTextField.js.map +1 -1
  56. package/dist/components/core/filter/fields/SolidShortTextField.tsx +2 -2
  57. package/dist/components/core/kanban/KanbanCard.d.ts.map +1 -1
  58. package/dist/components/core/kanban/KanbanCard.js +1 -1
  59. package/dist/components/core/kanban/KanbanCard.js.map +1 -1
  60. package/dist/components/core/kanban/KanbanCard.tsx +18 -16
  61. package/dist/components/core/kanban/SolidKanbanView.d.ts.map +1 -1
  62. package/dist/components/core/kanban/SolidKanbanView.js +67 -48
  63. package/dist/components/core/kanban/SolidKanbanView.js.map +1 -1
  64. package/dist/components/core/kanban/SolidKanbanView.tsx +51 -11
  65. package/dist/components/core/kanban/SolidKanbanViewConfigure.d.ts +1 -1
  66. package/dist/components/core/kanban/SolidKanbanViewConfigure.d.ts.map +1 -1
  67. package/dist/components/core/kanban/SolidKanbanViewConfigure.js +11 -2
  68. package/dist/components/core/kanban/SolidKanbanViewConfigure.js.map +1 -1
  69. package/dist/components/core/kanban/SolidKanbanViewConfigure.tsx +35 -0
  70. package/dist/components/core/list/SolidListView.d.ts.map +1 -1
  71. package/dist/components/core/list/SolidListView.js +6 -2
  72. package/dist/components/core/list/SolidListView.js.map +1 -1
  73. package/dist/components/core/list/SolidListView.tsx +7 -1
  74. package/dist/components/core/list/SolidListViewConfigure.d.ts.map +1 -1
  75. package/dist/components/core/list/SolidListViewConfigure.js +15 -11
  76. package/dist/components/core/list/SolidListViewConfigure.js.map +1 -1
  77. package/dist/components/core/list/SolidListViewConfigure.tsx +25 -6
  78. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.d.ts +1 -1
  79. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.d.ts.map +1 -1
  80. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.js +3 -1
  81. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.js.map +1 -1
  82. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.tsx +3 -1
  83. package/dist/components/core/list/columns/SolidMediaMultipleColumn.d.ts.map +1 -1
  84. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js +7 -0
  85. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js.map +1 -1
  86. package/dist/components/core/list/columns/SolidMediaMultipleColumn.tsx +5 -0
  87. package/dist/components/core/list/columns/SolidMediaSingleColumn.d.ts.map +1 -1
  88. package/dist/components/core/list/columns/SolidMediaSingleColumn.js +3 -0
  89. package/dist/components/core/list/columns/SolidMediaSingleColumn.js.map +1 -1
  90. package/dist/components/core/list/columns/SolidMediaSingleColumn.tsx +2 -0
  91. package/dist/components/core/list/widgets/SolidShortTextFieldImageRenderModeWidget.d.ts.map +1 -1
  92. package/dist/components/core/list/widgets/SolidShortTextFieldImageRenderModeWidget.js +3 -0
  93. package/dist/components/core/list/widgets/SolidShortTextFieldImageRenderModeWidget.js.map +1 -1
  94. package/dist/components/core/list/widgets/SolidShortTextFieldImageRenderModeWidget.tsx +2 -0
  95. package/dist/components/core/model/CreateModel.css +10 -0
  96. package/dist/components/core/model/FieldMetaDataForm.css +10 -0
  97. package/dist/components/core/model/FieldMetaDataForm.js +8 -8
  98. package/dist/components/core/model/FieldMetaDataForm.js.map +1 -1
  99. package/dist/components/core/model/FieldMetaDataForm.tsx +13 -13
  100. package/dist/components/core/module/ModuleMetadataExplorer.css +23 -0
  101. package/dist/components/core/module/ModuleMetadataExplorer.d.ts.map +1 -1
  102. package/dist/components/core/module/ModuleMetadataExplorer.js +1 -1
  103. package/dist/components/core/module/ModuleMetadataExplorer.js.map +1 -1
  104. package/dist/components/core/module/ModuleMetadataExplorer.tsx +9 -0
  105. package/dist/components/core/tree/SolidTreeView.d.ts.map +1 -1
  106. package/dist/components/core/tree/SolidTreeView.js +249 -138
  107. package/dist/components/core/tree/SolidTreeView.js.map +1 -1
  108. package/dist/components/core/tree/SolidTreeView.tsx +220 -92
  109. package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.d.ts.map +1 -1
  110. package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.js +1 -1
  111. package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.js.map +1 -1
  112. package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx +1 -0
  113. package/dist/components/layout/AdminHeaderActions.js +6 -13
  114. package/dist/components/layout/AdminHeaderActions.js.map +1 -1
  115. package/dist/components/layout/AdminHeaderActions.tsx +11 -11
  116. package/dist/components/layout/AdminLayout.d.ts.map +1 -1
  117. package/dist/components/layout/AdminLayout.js +2 -2
  118. package/dist/components/layout/AdminLayout.js.map +1 -1
  119. package/dist/components/layout/AdminLayout.tsx +5 -4
  120. package/dist/components/layout/AppSidebar.d.ts.map +1 -1
  121. package/dist/components/layout/AppSidebar.js +17 -2
  122. package/dist/components/layout/AppSidebar.js.map +1 -1
  123. package/dist/components/layout/AppSidebar.tsx +21 -2
  124. package/dist/components/shad-cn-ui/SolidDatePicker.d.ts +1 -1
  125. package/dist/components/shad-cn-ui/SolidDatePicker.d.ts.map +1 -1
  126. package/dist/components/shad-cn-ui/SolidDatePicker.js +35 -3
  127. package/dist/components/shad-cn-ui/SolidDatePicker.js.map +1 -1
  128. package/dist/components/shad-cn-ui/SolidDatePicker.tsx +84 -0
  129. package/dist/components/shad-cn-ui/SolidTooltip.d.ts.map +1 -1
  130. package/dist/components/shad-cn-ui/SolidTooltip.js +55 -2
  131. package/dist/components/shad-cn-ui/SolidTooltip.js.map +1 -1
  132. package/dist/components/shad-cn-ui/SolidTooltip.tsx +95 -1
  133. package/dist/helpers/downloadMediaFile.d.ts.map +1 -1
  134. package/dist/helpers/downloadMediaFile.js +80 -5
  135. package/dist/helpers/downloadMediaFile.js.map +1 -1
  136. package/dist/helpers/downloadMediaFile.tsx +33 -6
  137. package/dist/resources/globals.css +109 -1
  138. package/dist/resources/solid-custom.css +53 -3
  139. package/dist/resources/solid-tailwind-generated.css +1 -1
  140. package/dist/routes/pages/admin/core/DashboardPage.d.ts.map +1 -1
  141. package/dist/routes/pages/admin/core/DashboardPage.js +2 -2
  142. package/dist/routes/pages/admin/core/DashboardPage.js.map +1 -1
  143. package/dist/routes/pages/admin/core/DashboardPage.tsx +2 -0
  144. package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.d.ts.map +1 -1
  145. package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.js +1 -1
  146. package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.js.map +1 -1
  147. package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.tsx +1 -0
  148. package/dist/types/css-modules.d.ts +2 -0
  149. package/package.json +1 -1
@@ -27,19 +27,24 @@ import CompactImage from '../../../resources/images/layout/images/compact.png';
27
27
  import CozyImage from '../../../resources/images/layout/images/cozy.png';
28
28
  import ComfortableImage from '../../../resources/images/layout/images/comfortable.png';
29
29
  import { ERROR_MESSAGES } from "../../../constants/error-messages";
30
- import { getFilterObjectFromLocalStorage, hasStoredFilterPredicates, setFilterObjectToLocalStorage } from "../common/globalSearchPersistence";
30
+ import { getFilterObjectFromLocalStorage, hasStoredFilterPredicates, hasStoredSearchUiState, setFilterObjectToLocalStorage } from "../common/globalSearchPersistence";
31
31
  import { SolidBeforeTreeNodeLoad } from "../../../types";
32
32
  import { getExtensionFunction } from "../../../helpers/registry";
33
33
  import { SolidTreeLoad, SolidTreeUiEventResponse } from "../../../types/solid-core";
34
+ import { getMediaTypeFromUrl } from "../../../helpers/mediaType";
34
35
  import { useRouter } from "../../../hooks/useRouter";
35
36
  import { normalizeSolidListTreeKanbanActionPath } from "../../../helpers/routePaths";
36
37
  import { usePathname } from "../../../hooks/usePathname";
37
38
  import { useHandleListCustomButtonClick } from "../../../components/common/useHandleListCustomButtonClick";
38
- import { SolidButton, SolidDialog, SolidDialogBody, SolidDialogDescription, SolidDialogFooter, SolidDialogHeader, SolidDialogSeparator, SolidDialogTitle, SolidDropdownMenu, SolidDropdownMenuContent, SolidDropdownMenuItem, SolidDropdownMenuSeparator, SolidDropdownMenuTrigger, SolidIcon, SolidSelect } from "../../shad-cn-ui";
39
+ import { isButtonVisibleInCurrentEnv } from "../../../helpers/buttonEnvironment";
40
+ import { SolidButton, SolidConfirmDialog, SolidDialog, SolidDialogBody, SolidDialogDescription, SolidDialogFooter, SolidDialogHeader, SolidDialogSeparator, SolidDialogTitle, SolidDropdownMenu, SolidDropdownMenuContent, SolidDropdownMenuItem, SolidDropdownMenuSeparator, SolidDropdownMenuTrigger, SolidIcon, SolidSelect } from "../../shad-cn-ui";
39
41
  import { SolidHeaderRequestStatus } from "../../common/SolidHeaderRequestStatus";
42
+ import { SolidLightbox } from "../../shad-cn-ui/SolidLightbox";
43
+ import type { SolidLightboxSlide } from "../../shad-cn-ui/SolidLightbox";
40
44
  import { storeCurrentModelViewContext } from "../../../helpers/modelViewPersistence";
41
45
  import { getRelationDisplayText } from "../../../helpers/relationDisplay";
42
46
  import { Column as SolidTreeColumn, SolidTreeNode as TreeNode, SolidTreeSelectionKeys, SolidTreeTable } from "./SolidTreeTable";
47
+ import { SolidListViewHeaderButton } from "../list/SolidListViewHeaderButton";
43
48
 
44
49
  // ─── Types ────────────────────────────────────────────────────────────────────
45
50
 
@@ -160,6 +165,8 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
160
165
  const [isDeleteRecordsDialogVisible, setDeleteRecordsDialogVisible] = useState(false);
161
166
  const [isRecoverDialogVisible, setRecoverDialogVisible] = useState(false);
162
167
  const [showArchived, setShowArchived] = useState(false);
168
+ const [openLightbox, setOpenLightbox] = useState(false);
169
+ const [lightboxUrls, setLightboxUrls] = useState<any[]>([]);
163
170
 
164
171
 
165
172
  const [createButtonUrl, setCreateButtonUrl] = useState<string>();
@@ -178,6 +185,25 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
178
185
 
179
186
  const [size, setSize] = useState<string | any>(sizeOptions[1].value);
180
187
  const [viewModes, setViewModes] = useState<any>([]);
188
+ const lightboxSlides: SolidLightboxSlide[] = Array.isArray(lightboxUrls)
189
+ ? lightboxUrls
190
+ .map((item: any) => {
191
+ const src = item?.src || item?.downloadUrl || "";
192
+ if (!src) {
193
+ return null;
194
+ }
195
+
196
+ const mediaType = getMediaTypeFromUrl(src);
197
+ const slide: SolidLightboxSlide = { src };
198
+
199
+ if (mediaType !== "image") {
200
+ slide.type = mediaType;
201
+ }
202
+
203
+ return slide;
204
+ })
205
+ .filter((slide): slide is SolidLightboxSlide => !!slide)
206
+ : [];
181
207
 
182
208
  const headerRequestStatusLabel = treeLoading ? "Loading..." : null;
183
209
 
@@ -604,6 +630,18 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
604
630
  groupPath
605
631
  .map((item) => buildImplicitFilterCondition(item));
606
632
 
633
+ const getGroupNodeKey = (groupPath: GroupPathItem[] = []) =>
634
+ groupPath.reduce(
635
+ (parentKey, item) => `${parentKey}-g-${encodeURIComponent(JSON.stringify({
636
+ ruleIndex: item.ruleIndex,
637
+ fieldName: item.fieldName,
638
+ filterField: item.filterField,
639
+ value: item.value,
640
+ dateGranularity: item.dateGranularity ?? null,
641
+ }))}`,
642
+ "root",
643
+ );
644
+
607
645
  const mergeFiltersWithImplicit = (implicitFilters: any[]) => {
608
646
  const baseFilters = latestFiltersRef.current;
609
647
  const hasBaseFilters = baseFilters && Object.keys(baseFilters).length > 0;
@@ -759,7 +797,8 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
759
797
 
760
798
  const queryString = qs.stringify(queryData, { encodeValuesOnly: true });
761
799
 
762
- const response = await triggerGetSolidEntities(queryString).unwrap();
800
+ const request = triggerGetSolidEntities(queryString);
801
+ const response = await request.unwrap().finally(() => request.unsubscribe());
763
802
  return { queryData, queryString, response };
764
803
  };
765
804
 
@@ -825,7 +864,8 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
825
864
 
826
865
 
827
866
 
828
- const response = await triggerGetSolidEntities(queryString).unwrap();
867
+ const request = triggerGetSolidEntities(queryString);
868
+ const response = await request.unwrap().finally(() => request.unsubscribe());
829
869
  return { queryData, queryString, response };
830
870
  };
831
871
 
@@ -835,7 +875,6 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
835
875
  groupMetaRows: any[],
836
876
  ruleIndex: number,
837
877
  parentPath: GroupPathItem[],
838
- parentKey: string
839
878
  ): TreeNode[] => {
840
879
  const rule = activeGroupingRules[ruleIndex];
841
880
  if (!rule?.fieldName) return [];
@@ -844,7 +883,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
844
883
  const filterField = getResolvedGroupField(fieldName);
845
884
  // const dateGranularity = dateTimeImplicitFilter(rule);
846
885
  const dateGranularity = getDateGranularity(rule);
847
- return (groupMetaRows || []).map((groupMeta, index) => {
886
+ return (groupMetaRows || []).map((groupMeta) => {
848
887
  const rawGroupLabel = groupMeta?.groupName;
849
888
  const normalizedGroupLabel =
850
889
  rawGroupLabel === "[object Object]"
@@ -860,7 +899,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
860
899
  ];
861
900
 
862
901
  return {
863
- key: `${parentKey}-g-${ruleIndex}-${index}`,
902
+ key: getGroupNodeKey(groupPath),
864
903
  data: {
865
904
  __treeMeta: {
866
905
  nodeType: "group",
@@ -915,12 +954,60 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
915
954
  });
916
955
  };
917
956
 
957
+ const fetchNodeChildren = async (node: TreeNode,offset: number,limit: number,): Promise<{ children: TreeNode[]; total: number }> => {
958
+ const meta = (node.data as TreeRowData)?.__treeMeta;
959
+ if (!meta || meta.nodeType !== "group") {
960
+ return { children: [], total: 0 };
961
+ }
962
+
963
+ const nodeKey = String(node.key);
964
+ const nextRuleIndex = meta.ruleIndex + 1;
965
+
966
+ if (nextRuleIndex < activeGroupingRules.length) {
967
+ const { response } = await runGroupedQuery(
968
+ nextRuleIndex,
969
+ meta.groupPath || [],
970
+ offset,
971
+ limit
972
+ );
973
+
974
+ return {
975
+ children: buildGroupNodes(
976
+ response?.groupMeta || [],
977
+ nextRuleIndex,
978
+ meta.groupPath || [],
979
+ ),
980
+ total: response?.meta.totalRecords ?? 0,
981
+ };
982
+ }
983
+
984
+ const { response } = await runLeafQuery(meta.groupPath || [], offset, limit);
985
+ return {
986
+ children: buildRecordNodes(response?.records || [], nodeKey, meta.groupPath || []),
987
+ total: response?.meta.totalRecords ?? 0,
988
+ };
989
+ };
990
+
991
+ const mergeNodesWithCurrentState = (freshNodes: TreeNode[], currentNodes: TreeNode[]) => {
992
+ const currentNodesByKey = new Map(
993
+ currentNodes.map((node) => [String(node.key), node]),
994
+ );
995
+
996
+ return freshNodes.map((node) => {
997
+ const currentNode = currentNodesByKey.get(String(node.key));
998
+ return currentNode?.children?.length
999
+ ? { ...node, children: currentNode.children }
1000
+ : node;
1001
+ });
1002
+ };
1003
+
918
1004
  // ─── Root group load / paginate ───────────────────────────────────────────
919
1005
 
920
- const loadRootGroups = async (offset = 0) => {
1006
+ const loadRootGroups = async (offset = 0, affectedGroupPaths: GroupPathItem[][] = []) => {
921
1007
  if (!solidTreeViewMetaData || activeGroupingRules.length === 0) {
922
1008
  setTreeNodes([]);
923
1009
  setExpandedKeys({});
1010
+ setPaginationMap({});
924
1011
 
925
1012
  const queryObject = getFilterObjectFromLocalStorage();
926
1013
  if (queryObject) {
@@ -936,13 +1023,46 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
936
1023
  setTreeLoading(true);
937
1024
  try {
938
1025
  const { response } = await runGroupedQuery(0, [], offset, limit);
939
- const rootNodes = buildGroupNodes(response?.groupMeta || [], 0, [], "root");
940
- setTreeNodes(rootNodes);
941
- setExpandedKeys({});
942
- setSelectedNodeKeys({});
943
- // Collapse expanded keys since data changed
1026
+ const rootNodes = buildGroupNodes(response?.groupMeta || [], 0, []);
944
1027
  const total = response?.meta.totalRecords ?? 0;
945
- setPagination("root", { offset, total: total, limit });
1028
+ const rootPaginationEntry: PaginationEntry = { offset, total, limit };
1029
+
1030
+ let nextTreeNodes = affectedGroupPaths.length > 0
1031
+ ? mergeNodesWithCurrentState(rootNodes, treeNodes)
1032
+ : rootNodes;
1033
+ const nextPaginationMap: Record<string, PaginationEntry> = affectedGroupPaths.length > 0
1034
+ ? { ...paginationMap, root: rootPaginationEntry }
1035
+ : { root: rootPaginationEntry };
1036
+ const refreshedNodeKeys = new Set<string>();
1037
+
1038
+ for (const groupPath of affectedGroupPaths) {
1039
+ for (let depth = 0; depth < groupPath.length; depth += 1) {
1040
+ const nodeKey = getGroupNodeKey(groupPath.slice(0, depth + 1));
1041
+ if (refreshedNodeKeys.has(nodeKey)) continue;
1042
+
1043
+ const node = findNodeByKey(nextTreeNodes, nodeKey);
1044
+ if (!node) break;
1045
+
1046
+ const pagination = getPagination(nodeKey);
1047
+ const { children, total: childTotal } = await fetchNodeChildren(
1048
+ node,
1049
+ pagination.offset,
1050
+ pagination.limit,
1051
+ );
1052
+ const nextChildren = depth < groupPath.length - 1
1053
+ ? mergeNodesWithCurrentState(children, node.children || [])
1054
+ : children;
1055
+
1056
+ nextTreeNodes = updateNodeChildren(nextTreeNodes, nodeKey, nextChildren);
1057
+ nextPaginationMap[nodeKey] = { ...pagination, total: childTotal };
1058
+ refreshedNodeKeys.add(nodeKey);
1059
+ }
1060
+ }
1061
+
1062
+ setTreeNodes(nextTreeNodes);
1063
+ setSelectedNodeKeys({});
1064
+ setPaginationMap(nextPaginationMap);
1065
+ if (affectedGroupPaths.length === 0) setExpandedKeys({});
946
1066
 
947
1067
  if (latestFilterPredicatesRef.current && latestFilterPredicatesRef.current.persistFilter) {
948
1068
  let queryData: any = {
@@ -966,7 +1086,6 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
966
1086
  }
967
1087
 
968
1088
  } catch (error: any) {
969
- setTreeNodes([]);
970
1089
  dispatch(showToast({ severity: "error", summary: "Failed to load tree", detail: error?.data?.message || error?.message || "Unable to load grouped data", life: 4000 }));
971
1090
  } finally {
972
1091
  setTreeLoading(false);
@@ -990,33 +1109,10 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
990
1109
 
991
1110
  const nodeKey = String(node.key);
992
1111
  const limit = getPagination(nodeKey).limit || globalLimit || DEFAULT_PAGE_SIZE;
993
- const nextRuleIndex = meta.ruleIndex + 1;
994
1112
 
995
1113
  setTreeLoading(true);
996
1114
  try {
997
- let children: TreeNode[] = [];
998
- let total = 0;
999
-
1000
- if (nextRuleIndex < activeGroupingRules.length) {
1001
-
1002
- const { response } = await runGroupedQuery(
1003
- nextRuleIndex,
1004
- meta.groupPath || [],
1005
- offset,
1006
- limit
1007
- );
1008
- children = buildGroupNodes(
1009
- response?.groupMeta || [],
1010
- nextRuleIndex,
1011
- meta.groupPath || [],
1012
- nodeKey
1013
- );
1014
- total = response?.meta.totalRecords ?? 0;
1015
- } else {
1016
- const { response } = await runLeafQuery(meta.groupPath || [], offset, limit);
1017
- children = buildRecordNodes(response?.records || [], nodeKey, meta.groupPath || []);
1018
- total = response?.meta.totalRecords ?? 0;
1019
- }
1115
+ const { children, total } = await fetchNodeChildren(node, offset, limit);
1020
1116
 
1021
1117
  setTreeNodes((prev) => updateNodeChildren(prev, nodeKey, children));
1022
1118
  setPagination(node.key as string, { offset, total: total, limit });
@@ -1213,6 +1309,9 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1213
1309
  // handle bulk deletion
1214
1310
  const deleteBulk = async () => {
1215
1311
  const deleteList = selectedRecords.map((element: any) => element.id);
1312
+ const affectedGroupPaths = selectedRecords
1313
+ .map((record: TreeRowData) => record?.__treeMeta?.groupPath)
1314
+ .filter((groupPath): groupPath is GroupPathItem[] => Array.isArray(groupPath));
1216
1315
  if (deleteList.length === 0) {
1217
1316
  setDeleteRecordsDialogVisible(false);
1218
1317
  return;
@@ -1222,7 +1321,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1222
1321
  await deleteManySolidEntities(deleteList).unwrap();
1223
1322
  dispatch(showToast({ severity: 'success', summary: 'Deleted', detail: ERROR_MESSAGES.RECORD_DELETE, life: 3000 }));
1224
1323
  setDeleteRecordsDialogVisible(false);
1225
- await loadRootGroups(getPagination("root").offset);
1324
+ await loadRootGroups(getPagination("root").offset, affectedGroupPaths);
1226
1325
  } catch (error: any) {
1227
1326
  dispatch(showToast({ severity: 'error', summary: 'Delete Failed', detail: error?.data?.message, life: 4000 }));
1228
1327
  }
@@ -1238,6 +1337,9 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1238
1337
 
1239
1338
  const recoverAll = async () => {
1240
1339
  const recoverList = selectedRecoverRecords.map((element: any) => element.id);
1340
+ const affectedGroupPaths = selectedRecoverRecords
1341
+ .map((record: TreeRowData) => record?.__treeMeta?.groupPath)
1342
+ .filter((groupPath): groupPath is GroupPathItem[] => Array.isArray(groupPath));
1241
1343
  if (recoverList.length === 0) {
1242
1344
  setRecoverDialogVisible(false);
1243
1345
  return;
@@ -1252,7 +1354,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1252
1354
  detail: response?.data?.message || "Records recovered successfully.",
1253
1355
  life: 3000,
1254
1356
  }));
1255
- await loadRootGroups(getPagination("root").offset);
1357
+ await loadRootGroups(getPagination("root").offset, affectedGroupPaths);
1256
1358
  } catch (error: any) {
1257
1359
  dispatch(showToast({
1258
1360
  severity: "error",
@@ -1292,8 +1394,8 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1292
1394
  solidListViewMetaData: solidTreeViewMetaData,
1293
1395
  fieldMetadata,
1294
1396
  column,
1295
- setLightboxUrls: () => { },
1296
- setOpenLightbox: () => { },
1397
+ setLightboxUrls,
1398
+ setOpenLightbox,
1297
1399
  });
1298
1400
 
1299
1401
  if (!React.isValidElement(listColumn)) return null;
@@ -1490,9 +1592,19 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1490
1592
 
1491
1593
 
1492
1594
  const handleCustomButtonClick = useHandleListCustomButtonClick();
1595
+ const shouldShowMobileSearchElement = showGlobalSearchElement || hasStoredSearchUiState(filterPredicates) || hasStoredSearchUiState(getFilterObjectFromLocalStorage());
1596
+
1597
+ const visibleHeaderButtons = useMemo(
1598
+ () =>
1599
+ (solidTreeViewLayout?.attrs?.headerButtons ?? []).filter(
1600
+ (button: any) => isButtonVisibleInCurrentEnv(button?.attrs),
1601
+ ),
1602
+ [solidTreeViewLayout?.attrs?.headerButtons],
1603
+ );
1493
1604
 
1494
1605
  const [selectedSolidViewData, setSelectedSolidViewData] = useState<any>();
1495
1606
  const [deleteEntity, setDeleteEntity] = useState(false);
1607
+ const entityDisplayName = solidTreeViewMetaData?.data?.solidView?.model?.displayName || params?.modelName;
1496
1608
  const openRowForEdit = (rowData: any, viewMode: "edit" | "view") => {
1497
1609
  storeCurrentModelViewContext();
1498
1610
 
@@ -1537,13 +1649,16 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1537
1649
  throw new Error(ERROR_MESSAGES.NO_ENTITY_SELECTED);
1538
1650
  }
1539
1651
 
1652
+ const affectedGroupPath = selectedSolidViewData?.__treeMeta?.groupPath;
1540
1653
  const response: any = await deleteSolidSingleEntiry(selectedSolidViewData.id);
1541
-
1542
1654
  if (response?.data?.statusCode === 200) {
1543
1655
  setDeleteEntity(false);
1544
1656
  setSelectedSolidViewData(undefined);
1545
1657
  dispatch(showToast({ severity: "success", summary: ERROR_MESSAGES.DELETED, detail: ERROR_MESSAGES.ENTITY_DELETE, life: 3000 }));
1546
- await loadRootGroups(getPagination("root").offset);
1658
+ await loadRootGroups(
1659
+ getPagination("root").offset,
1660
+ Array.isArray(affectedGroupPath) ? [affectedGroupPath] : [],
1661
+ );
1547
1662
  } else {
1548
1663
  dispatch(showToast({ severity: "error", summary: ERROR_MESSAGES.DELETE_FAIELD, detail: response?.error?.data?.error, sticky: true }));
1549
1664
  }
@@ -1563,7 +1678,10 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1563
1678
  detail: response?.data?.message || "Record recovered successfully.",
1564
1679
  life: 3000,
1565
1680
  }));
1566
- await loadRootGroups(getPagination("root").offset);
1681
+ await loadRootGroups(
1682
+ getPagination("root").offset,
1683
+ Array.isArray(record?.__treeMeta?.groupPath) ? [record.__treeMeta.groupPath] : [],
1684
+ );
1567
1685
  } catch (error: any) {
1568
1686
  dispatch(showToast({
1569
1687
  severity: "error",
@@ -1768,6 +1886,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1768
1886
  ref={solidGlobalSearchElementRef}
1769
1887
  viewData={solidTreeViewMetaData}
1770
1888
  handleApplyCustomFilter={handleApplyCustomFilter}
1889
+ filterPredicates={filterPredicates}
1771
1890
  />
1772
1891
  </div>
1773
1892
  )}
@@ -1789,6 +1908,22 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1789
1908
  </div>
1790
1909
  )}
1791
1910
 
1911
+ <div className="solid-header-buttons-wrapper hidden items-center lg:flex">
1912
+ {visibleHeaderButtons
1913
+ ?.filter((button: any) => button?.attrs?.actionInContextMenu !== true)
1914
+ ?.map((button: any, index: number) => (
1915
+ <SolidListViewHeaderButton
1916
+ key={index}
1917
+ button={button}
1918
+ params={params}
1919
+ solidListViewMetaData={solidTreeViewMetaData}
1920
+ handleCustomButtonClick={handleCustomButtonClick}
1921
+ selectedRecords={selectedRecords}
1922
+ filters={filters}
1923
+ />
1924
+ ))}
1925
+ </div>
1926
+
1792
1927
  {actionsAllowed.includes(`${permissionExpression(params.modelName, "create")}`) &&
1793
1928
  solidTreeViewMetaData?.data?.solidView?.layout?.attrs.create !== false && (
1794
1929
  <SolidCreateButton
@@ -1858,7 +1993,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1858
1993
  </div>
1859
1994
 
1860
1995
  {solidTreeViewMetaData?.data?.solidView?.layout?.attrs.enableGlobalSearch === true &&
1861
- showGlobalSearchElement && (
1996
+ shouldShowMobileSearchElement && (
1862
1997
  <div className="flex lg:hidden">
1863
1998
  <SolidGlobalSearchElement
1864
1999
  viewType="tree"
@@ -1867,6 +2002,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1867
2002
  ref={solidGlobalSearchElementRef}
1868
2003
  viewData={solidTreeViewMetaData}
1869
2004
  handleApplyCustomFilter={handleApplyCustomFilter}
2005
+ filterPredicates={filterPredicates}
1870
2006
  />
1871
2007
  </div>
1872
2008
  )}
@@ -2050,53 +2186,37 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
2050
2186
  </div>
2051
2187
  </div>
2052
2188
 
2053
- <SolidDialog
2189
+ <SolidConfirmDialog
2054
2190
  open={deleteEntity}
2055
- onOpenChange={(open) => {
2056
- if (!open) setDeleteEntity(false);
2057
- }}
2058
- contentClassName="solid-confirm-dialog solid-tree-confirm-dialog"
2059
- showHeader={false}
2060
- >
2061
- <SolidDialogHeader>
2062
- <SolidDialogTitle>
2063
- Delete {solidTreeViewMetaData?.data?.solidView?.model?.displayName || params?.modelName}
2064
- </SolidDialogTitle>
2065
- </SolidDialogHeader>
2066
- <SolidDialogSeparator />
2067
- <SolidDialogBody>
2068
- <SolidDialogDescription className="m-0">
2069
- {`Are you sure you want to delete this ${solidTreeViewMetaData?.data?.solidView?.model?.displayName || params?.modelName}?`}
2070
- </SolidDialogDescription>
2071
- </SolidDialogBody>
2072
- <SolidDialogFooter>
2073
- <SolidButton label="Cancel" size="sm" variant="outline" onClick={() => setDeleteEntity(false)} />
2074
- <SolidButton label="Delete" size="sm" severity="danger" onClick={handleDeleteEntity} />
2075
- </SolidDialogFooter>
2076
- </SolidDialog>
2077
-
2078
- <SolidDialog
2191
+ onCancel={() => setDeleteEntity(false)}
2192
+ onConfirm={handleDeleteEntity}
2193
+ className="solid-shadcn-confirm-dialog solid-delete-confirm-dialog"
2194
+ headerClassName="solid-shadcn-dialog-head"
2195
+ bodyClassName="solid-shadcn-dialog-body"
2196
+ footerClassName="solid-shadcn-dialog-actions"
2197
+ separatorClassName="solid-shadcn-dialog-sep"
2198
+ showSeparator
2199
+ title={`Delete ${entityDisplayName}`}
2200
+ message={<p className="solid-shadcn-dialog-text">{`Are you sure you want to delete this ${entityDisplayName}?`}</p>}
2201
+ confirmLabel="Delete"
2202
+ cancelLabel="Cancel"
2203
+ />
2204
+
2205
+ <SolidConfirmDialog
2079
2206
  open={isDeleteRecordsDialogVisible}
2080
- onOpenChange={(open) => {
2081
- if (!open) setDeleteRecordsDialogVisible(false);
2082
- }}
2083
- contentClassName="solid-confirm-dialog solid-tree-confirm-dialog"
2084
- showHeader={false}
2085
- >
2086
- <SolidDialogHeader>
2087
- <SolidDialogTitle>Confirm Delete</SolidDialogTitle>
2088
- </SolidDialogHeader>
2089
- <SolidDialogSeparator />
2090
- <SolidDialogBody>
2091
- <SolidDialogDescription className="m-0">
2092
- Are you sure you want to delete the selected records?
2093
- </SolidDialogDescription>
2094
- </SolidDialogBody>
2095
- <SolidDialogFooter>
2096
- <SolidButton label="Cancel" size="sm" variant="outline" onClick={onDeleteClose} />
2097
- <SolidButton label="Delete" size="sm" severity="danger" onClick={deleteBulk} />
2098
- </SolidDialogFooter>
2099
- </SolidDialog>
2207
+ onCancel={onDeleteClose}
2208
+ onConfirm={deleteBulk}
2209
+ className="solid-shadcn-confirm-dialog solid-delete-confirm-dialog"
2210
+ headerClassName="solid-shadcn-dialog-head"
2211
+ bodyClassName="solid-shadcn-dialog-body"
2212
+ footerClassName="solid-shadcn-dialog-actions"
2213
+ separatorClassName="solid-shadcn-dialog-sep"
2214
+ showSeparator
2215
+ title="Delete Records"
2216
+ message={<p className="solid-shadcn-dialog-text">Are you sure you want to delete the selected records?</p>}
2217
+ confirmLabel="Delete"
2218
+ cancelLabel="Cancel"
2219
+ />
2100
2220
 
2101
2221
  <SolidDialog
2102
2222
  open={isRecoverDialogVisible}
@@ -2121,6 +2241,14 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
2121
2241
  </SolidDialogFooter>
2122
2242
  </SolidDialog>
2123
2243
 
2244
+ {openLightbox && (
2245
+ <SolidLightbox
2246
+ open={openLightbox}
2247
+ slides={lightboxSlides}
2248
+ onClose={() => setOpenLightbox(false)}
2249
+ />
2250
+ )}
2251
+
2124
2252
  </div>
2125
2253
  );
2126
2254
  });
@@ -1 +1 @@
1
- {"version":3,"file":"GenerateApiKeyModal.d.ts","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,YAAY,EAAoC,MAAM,iCAAiC,CAAC;AAiCtG,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAkIjG"}
1
+ {"version":3,"file":"GenerateApiKeyModal.d.ts","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,YAAY,EAAoC,MAAM,iCAAiC,CAAC;AAiCtG,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAmIjG"}
@@ -152,6 +152,6 @@ export function GenerateApiKeyModal(_a) {
152
152
  formik.setFieldValue("expiryOption", value);
153
153
  if (value !== "custom")
154
154
  setCustomDate(null);
155
- } }), formik.values.expiryOption === "custom" && (_jsx(SolidDatePicker, { selected: customDate, onChange: function (date) { return setCustomDate(date); }, minDate: new Date(), placeholderText: "Select expiry date", dateFormat: "dd/MM/yyyy" })), formik.values.expiryOption !== "custom" ? (_jsx("p", { className: "solid-api-key-dialog-hint m-0", children: "Choose how long this key should remain valid." })) : null, expiryError && _jsx(SolidMessage, { severity: "error", text: expiryError })] })] }) }) }), _jsx(SolidDialogSeparator, {}), _jsxs(SolidDialogFooter, { className: "solid-api-key-dialog-footer", children: [_jsx(SolidButton, { variant: "outline", type: "button", onClick: handleClose, disabled: isLoading, children: "Cancel" }), _jsx(SolidButton, { type: "submit", form: "generate-api-key-form", loading: isLoading, children: "Generate Key" })] })] }));
155
+ }, native: false }), formik.values.expiryOption === "custom" && (_jsx(SolidDatePicker, { selected: customDate, onChange: function (date) { return setCustomDate(date); }, minDate: new Date(), placeholderText: "Select expiry date", dateFormat: "dd/MM/yyyy" })), formik.values.expiryOption !== "custom" ? (_jsx("p", { className: "solid-api-key-dialog-hint m-0", children: "Choose how long this key should remain valid." })) : null, expiryError && _jsx(SolidMessage, { severity: "error", text: expiryError })] })] }) }) }), _jsx(SolidDialogSeparator, {}), _jsxs(SolidDialogFooter, { className: "solid-api-key-dialog-footer", children: [_jsx(SolidButton, { variant: "outline", type: "button", onClick: handleClose, disabled: isLoading, children: "Cancel" }), _jsx(SolidButton, { type: "submit", form: "generate-api-key-form", loading: isLoading, children: "Generate Key" })] })] }));
156
156
  }
157
157
  //# sourceMappingURL=GenerateApiKeyModal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GenerateApiKeyModal.js","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useDispatch } from \"react-redux\";\nimport { useFormik } from \"formik\";\nimport * as Yup from \"yup\";\nimport {\n SolidButton,\n SolidDatePicker,\n SolidDialog,\n SolidDialogBody,\n SolidDialogDescription,\n SolidDialogFooter,\n SolidDialogHeader,\n SolidDialogSeparator,\n SolidDialogTitle,\n SolidInput,\n SolidMessage,\n SolidSelect,\n} from \"../../../shad-cn-ui\";\nimport { showToast } from \"../../../../redux/features/toastSlice\";\nimport { type ApiKeyRecord, useGenerateApiKeyForUserMutation } from \"../../../../redux/api/apiKeyApi\";\n\nconst EXPIRY_OPTIONS = [\n { label: \"30 days\", value: \"30d\" },\n { label: \"60 days\", value: \"60d\" },\n { label: \"90 days\", value: \"90d\" },\n { label: \"Never\", value: \"never\" },\n { label: \"Custom date\", value: \"custom\" },\n];\n\nfunction addDays(days: number): string {\n const d = new Date();\n d.setDate(d.getDate() + days);\n return d.toISOString();\n}\n\nfunction resolveExpiresAt(expiryOption: string, customDate: Date | null): string | undefined {\n switch (expiryOption) {\n case \"30d\":\n return addDays(30);\n case \"60d\":\n return addDays(60);\n case \"90d\":\n return addDays(90);\n case \"never\":\n return undefined;\n case \"custom\":\n return customDate ? customDate.toISOString() : undefined;\n default:\n return undefined;\n }\n}\n\ninterface GenerateApiKeyModalProps {\n open: boolean;\n onClose: () => void;\n onCreated: (rawApiKey: string, keyName: string, record: ApiKeyRecord) => void;\n userId?: number;\n}\n\nexport function GenerateApiKeyModal({ open, onClose, onCreated, userId }: GenerateApiKeyModalProps) {\n const dispatch = useDispatch();\n const [generateApiKeyForUser, { isLoading }] = useGenerateApiKeyForUserMutation();\n const [customDate, setCustomDate] = useState<Date | null>(null);\n\n const formik = useFormik({\n initialValues: {\n name: \"\",\n expiryOption: \"30d\",\n },\n validationSchema: Yup.object({\n name: Yup.string()\n .trim()\n .min(2, \"Name must be at least 2 characters\")\n .max(64, \"Name must be 64 characters or fewer\")\n .required(\"Key name is required\"),\n expiryOption: Yup.string().required(\"Expiry is required\"),\n }),\n onSubmit: async (values, helpers) => {\n if (values.expiryOption === \"custom\" && !customDate) {\n helpers.setFieldError(\"expiryOption\", \"Please select a custom expiry date\");\n return;\n }\n\n const expiresAt = resolveExpiresAt(values.expiryOption, customDate);\n\n try {\n const body = { name: values.name.trim(), ...(expiresAt ? { expiresAt } : {}) };\n const response = await generateApiKeyForUser({ userId: userId!, body }).unwrap();\n\n onCreated(response.data.apiKey, values.name.trim(), response.data.record);\n handleClose();\n } catch (err: any) {\n const detail = err?.data?.message || \"Failed to generate API key. Please try again.\";\n dispatch(showToast({ severity: \"error\", summary: \"Error\", detail }));\n }\n },\n });\n\n const handleClose = () => {\n formik.resetForm();\n setCustomDate(null);\n onClose();\n };\n\n // Reset form when modal opens\n useEffect(() => {\n if (open) {\n formik.resetForm();\n setCustomDate(null);\n }\n }, [open]);\n\n const nameError =\n formik.touched.name && formik.errors.name ? String(formik.errors.name) : \"\";\n const expiryError =\n formik.touched.expiryOption && formik.errors.expiryOption\n ? String(formik.errors.expiryOption)\n : \"\";\n\n return (\n <SolidDialog open={open} onOpenChange={handleClose} style={{ maxWidth: 480 }} className=\"solid-api-key-dialog\">\n <SolidDialogHeader className=\"solid-api-key-dialog-header\">\n <SolidDialogTitle>Generate API Key</SolidDialogTitle>\n <SolidDialogDescription>\n Create a named key for this user. The raw key will be shown only once after generation.\n </SolidDialogDescription>\n </SolidDialogHeader>\n <SolidDialogSeparator />\n <SolidDialogBody className=\"solid-api-key-dialog-body\">\n <form id=\"generate-api-key-form\" onSubmit={formik.handleSubmit}>\n <div className=\"solid-api-key-dialog-fields\">\n <div className=\"solid-api-key-dialog-field\">\n <label htmlFor=\"api-key-name\" className=\"form-field-label\">\n Key Name <span style={{ color: \"var(--solid-danger-color, #ef4444)\" }}>*</span>\n </label>\n <SolidInput\n id=\"api-key-name\"\n name=\"name\"\n autoComplete=\"off\"\n placeholder=\"e.g. CI/CD pipeline, Mobile app\"\n value={formik.values.name}\n onChange={formik.handleChange}\n onBlur={formik.handleBlur}\n />\n {nameError && <SolidMessage severity=\"error\" text={nameError} />}\n </div>\n\n <div className=\"solid-api-key-dialog-field\">\n <label htmlFor=\"api-key-expiry\" className=\"form-field-label\">\n Expires\n </label>\n <SolidSelect\n value={formik.values.expiryOption}\n options={EXPIRY_OPTIONS}\n optionLabel=\"label\"\n optionValue=\"value\"\n onChange={({ value }) => {\n formik.setFieldValue(\"expiryOption\", value);\n if (value !== \"custom\") setCustomDate(null);\n }}\n />\n {formik.values.expiryOption === \"custom\" && (\n <SolidDatePicker\n selected={customDate}\n onChange={(date: Date | null) => setCustomDate(date)}\n minDate={new Date()}\n placeholderText=\"Select expiry date\"\n dateFormat=\"dd/MM/yyyy\"\n />\n )}\n {formik.values.expiryOption !== \"custom\" ? (\n <p className=\"solid-api-key-dialog-hint m-0\">Choose how long this key should remain valid.</p>\n ) : null}\n {expiryError && <SolidMessage severity=\"error\" text={expiryError} />}\n </div>\n </div>\n </form>\n </SolidDialogBody>\n <SolidDialogSeparator />\n <SolidDialogFooter className=\"solid-api-key-dialog-footer\">\n <SolidButton variant=\"outline\" type=\"button\" onClick={handleClose} disabled={isLoading}>\n Cancel\n </SolidButton>\n <SolidButton type=\"submit\" form=\"generate-api-key-form\" loading={isLoading}>\n Generate Key\n </SolidButton>\n </SolidDialogFooter>\n </SolidDialog>\n );\n}\n"]}
1
+ {"version":3,"file":"GenerateApiKeyModal.js","sourceRoot":"","sources":["../../../../../src/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,eAAe,EACf,WAAW,EACX,eAAe,EACf,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;AAClE,OAAO,EAAqB,gCAAgC,EAAE,MAAM,iCAAiC,CAAC;AAEtG,IAAM,cAAc,GAAG;IACrB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE;IAClC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE;IAClC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE;IAClC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;IAClC,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE;CAC1C,CAAC;AAEF,SAAS,OAAO,CAAC,IAAY;IAC3B,IAAM,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;IACrB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;AACzB,CAAC;AAED,SAAS,gBAAgB,CAAC,YAAoB,EAAE,UAAuB;IACrE,QAAQ,YAAY,EAAE;QACpB,KAAK,KAAK;YACR,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;QACrB,KAAK,KAAK;YACR,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;QACrB,KAAK,KAAK;YACR,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;QACrB,KAAK,OAAO;YACV,OAAO,SAAS,CAAC;QACnB,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D;YACE,OAAO,SAAS,CAAC;KACpB;AACH,CAAC;AASD,MAAM,UAAU,mBAAmB,CAAC,EAA8D;IAAlG,iBAmIC;QAnIqC,IAAI,UAAA,EAAE,OAAO,aAAA,EAAE,SAAS,eAAA,EAAE,MAAM,YAAA;IACpE,IAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IACzB,IAAA,KAAyC,gCAAgC,EAAE,EAA1E,qBAAqB,QAAA,EAAI,SAAS,kBAAwC,CAAC;IAC5E,IAAA,KAA8B,QAAQ,CAAc,IAAI,CAAC,EAAxD,UAAU,QAAA,EAAE,aAAa,QAA+B,CAAC;IAEhE,IAAM,MAAM,GAAG,SAAS,CAAC;QACvB,aAAa,EAAE;YACb,IAAI,EAAE,EAAE;YACR,YAAY,EAAE,KAAK;SACpB;QACD,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC;YAC3B,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE;iBACf,IAAI,EAAE;iBACN,GAAG,CAAC,CAAC,EAAE,oCAAoC,CAAC;iBAC5C,GAAG,CAAC,EAAE,EAAE,qCAAqC,CAAC;iBAC9C,QAAQ,CAAC,sBAAsB,CAAC;YACnC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;SAC1D,CAAC;QACF,QAAQ,EAAE,UAAO,MAAM,EAAE,OAAO;;;;;;wBAC9B,IAAI,MAAM,CAAC,YAAY,KAAK,QAAQ,IAAI,CAAC,UAAU,EAAE;4BACnD,OAAO,CAAC,aAAa,CAAC,cAAc,EAAE,oCAAoC,CAAC,CAAC;4BAC5E,sBAAO;yBACR;wBAEK,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;;;;wBAG5D,IAAI,cAAK,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,WAAA,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAE,CAAC;wBAC9D,qBAAM,qBAAqB,CAAC,EAAE,MAAM,EAAE,MAAO,EAAE,IAAI,MAAA,EAAE,CAAC,CAAC,MAAM,EAAE,EAAA;;wBAA1E,QAAQ,GAAG,SAA+D;wBAEhF,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;wBAC1E,WAAW,EAAE,CAAC;;;;wBAER,MAAM,GAAG,CAAA,MAAA,KAAG,aAAH,KAAG,uBAAH,KAAG,CAAE,IAAI,0CAAE,OAAO,KAAI,+CAA+C,CAAC;wBACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,QAAA,EAAE,CAAC,CAAC,CAAC;;;;;aAExE;KACF,CAAC,CAAC;IAEH,IAAM,WAAW,GAAG;QAClB,MAAM,CAAC,SAAS,EAAE,CAAC;QACnB,aAAa,CAAC,IAAI,CAAC,CAAC;QACpB,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;IAEF,8BAA8B;IAC9B,SAAS,CAAC;QACR,IAAI,IAAI,EAAE;YACR,MAAM,CAAC,SAAS,EAAE,CAAC;YACnB,aAAa,CAAC,IAAI,CAAC,CAAC;SACrB;IACH,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,IAAM,SAAS,GACb,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,IAAM,WAAW,GACf,MAAM,CAAC,OAAO,CAAC,YAAY,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY;QACvD,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;QACpC,CAAC,CAAC,EAAE,CAAC;IAET,OAAO,CACL,MAAC,WAAW,IAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,SAAS,EAAC,sBAAsB,aAC5G,MAAC,iBAAiB,IAAC,SAAS,EAAC,6BAA6B,aACxD,KAAC,gBAAgB,mCAAoC,EACrD,KAAC,sBAAsB,0GAEE,IACP,EACpB,KAAC,oBAAoB,KAAG,EACxB,KAAC,eAAe,IAAC,SAAS,EAAC,2BAA2B,YACpD,eAAM,EAAE,EAAC,uBAAuB,EAAC,QAAQ,EAAE,MAAM,CAAC,YAAY,YAC5D,eAAK,SAAS,EAAC,6BAA6B,aAC1C,eAAK,SAAS,EAAC,4BAA4B,aACzC,iBAAO,OAAO,EAAC,cAAc,EAAC,SAAS,EAAC,kBAAkB,0BAC/C,eAAM,KAAK,EAAE,EAAE,KAAK,EAAE,oCAAoC,EAAE,kBAAU,IACzE,EACR,KAAC,UAAU,IACT,EAAE,EAAC,cAAc,EACjB,IAAI,EAAC,MAAM,EACX,YAAY,EAAC,KAAK,EAClB,WAAW,EAAC,iCAAiC,EAC7C,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EACzB,QAAQ,EAAE,MAAM,CAAC,YAAY,EAC7B,MAAM,EAAE,MAAM,CAAC,UAAU,GACzB,EACD,SAAS,IAAI,KAAC,YAAY,IAAC,QAAQ,EAAC,OAAO,EAAC,IAAI,EAAE,SAAS,GAAI,IAC5D,EAEN,eAAK,SAAS,EAAC,4BAA4B,aACzC,gBAAO,OAAO,EAAC,gBAAgB,EAAC,SAAS,EAAC,kBAAkB,wBAEpD,EACR,KAAC,WAAW,IACV,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,EACjC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAC,OAAO,EACnB,WAAW,EAAC,OAAO,EACnB,QAAQ,EAAE,UAAC,EAAS;gDAAP,KAAK,WAAA;4CAChB,MAAM,CAAC,aAAa,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;4CAC5C,IAAI,KAAK,KAAK,QAAQ;gDAAE,aAAa,CAAC,IAAI,CAAC,CAAC;wCAC9C,CAAC,EACD,MAAM,EAAE,KAAK,GACb,EACD,MAAM,CAAC,MAAM,CAAC,YAAY,KAAK,QAAQ,IAAI,CAC1C,KAAC,eAAe,IACd,QAAQ,EAAE,UAAU,EACpB,QAAQ,EAAE,UAAC,IAAiB,IAAK,OAAA,aAAa,CAAC,IAAI,CAAC,EAAnB,CAAmB,EACpD,OAAO,EAAE,IAAI,IAAI,EAAE,EACnB,eAAe,EAAC,oBAAoB,EACpC,UAAU,EAAC,YAAY,GACvB,CACH,EACA,MAAM,CAAC,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,CACzC,YAAG,SAAS,EAAC,+BAA+B,8DAAkD,CAC/F,CAAC,CAAC,CAAC,IAAI,EACP,WAAW,IAAI,KAAC,YAAY,IAAC,QAAQ,EAAC,OAAO,EAAC,IAAI,EAAE,WAAW,GAAI,IAChE,IACF,GACD,GACS,EAClB,KAAC,oBAAoB,KAAG,EACxB,MAAC,iBAAiB,IAAC,SAAS,EAAC,6BAA6B,aACxD,KAAC,WAAW,IAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,uBAExE,EACd,KAAC,WAAW,IAAC,IAAI,EAAC,QAAQ,EAAC,IAAI,EAAC,uBAAuB,EAAC,OAAO,EAAE,SAAS,6BAE5D,IACI,IACR,CACf,CAAC;AACJ,CAAC","sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useDispatch } from \"react-redux\";\nimport { useFormik } from \"formik\";\nimport * as Yup from \"yup\";\nimport {\n SolidButton,\n SolidDatePicker,\n SolidDialog,\n SolidDialogBody,\n SolidDialogDescription,\n SolidDialogFooter,\n SolidDialogHeader,\n SolidDialogSeparator,\n SolidDialogTitle,\n SolidInput,\n SolidMessage,\n SolidSelect,\n} from \"../../../shad-cn-ui\";\nimport { showToast } from \"../../../../redux/features/toastSlice\";\nimport { type ApiKeyRecord, useGenerateApiKeyForUserMutation } from \"../../../../redux/api/apiKeyApi\";\n\nconst EXPIRY_OPTIONS = [\n { label: \"30 days\", value: \"30d\" },\n { label: \"60 days\", value: \"60d\" },\n { label: \"90 days\", value: \"90d\" },\n { label: \"Never\", value: \"never\" },\n { label: \"Custom date\", value: \"custom\" },\n];\n\nfunction addDays(days: number): string {\n const d = new Date();\n d.setDate(d.getDate() + days);\n return d.toISOString();\n}\n\nfunction resolveExpiresAt(expiryOption: string, customDate: Date | null): string | undefined {\n switch (expiryOption) {\n case \"30d\":\n return addDays(30);\n case \"60d\":\n return addDays(60);\n case \"90d\":\n return addDays(90);\n case \"never\":\n return undefined;\n case \"custom\":\n return customDate ? customDate.toISOString() : undefined;\n default:\n return undefined;\n }\n}\n\ninterface GenerateApiKeyModalProps {\n open: boolean;\n onClose: () => void;\n onCreated: (rawApiKey: string, keyName: string, record: ApiKeyRecord) => void;\n userId?: number;\n}\n\nexport function GenerateApiKeyModal({ open, onClose, onCreated, userId }: GenerateApiKeyModalProps) {\n const dispatch = useDispatch();\n const [generateApiKeyForUser, { isLoading }] = useGenerateApiKeyForUserMutation();\n const [customDate, setCustomDate] = useState<Date | null>(null);\n\n const formik = useFormik({\n initialValues: {\n name: \"\",\n expiryOption: \"30d\",\n },\n validationSchema: Yup.object({\n name: Yup.string()\n .trim()\n .min(2, \"Name must be at least 2 characters\")\n .max(64, \"Name must be 64 characters or fewer\")\n .required(\"Key name is required\"),\n expiryOption: Yup.string().required(\"Expiry is required\"),\n }),\n onSubmit: async (values, helpers) => {\n if (values.expiryOption === \"custom\" && !customDate) {\n helpers.setFieldError(\"expiryOption\", \"Please select a custom expiry date\");\n return;\n }\n\n const expiresAt = resolveExpiresAt(values.expiryOption, customDate);\n\n try {\n const body = { name: values.name.trim(), ...(expiresAt ? { expiresAt } : {}) };\n const response = await generateApiKeyForUser({ userId: userId!, body }).unwrap();\n\n onCreated(response.data.apiKey, values.name.trim(), response.data.record);\n handleClose();\n } catch (err: any) {\n const detail = err?.data?.message || \"Failed to generate API key. Please try again.\";\n dispatch(showToast({ severity: \"error\", summary: \"Error\", detail }));\n }\n },\n });\n\n const handleClose = () => {\n formik.resetForm();\n setCustomDate(null);\n onClose();\n };\n\n // Reset form when modal opens\n useEffect(() => {\n if (open) {\n formik.resetForm();\n setCustomDate(null);\n }\n }, [open]);\n\n const nameError =\n formik.touched.name && formik.errors.name ? String(formik.errors.name) : \"\";\n const expiryError =\n formik.touched.expiryOption && formik.errors.expiryOption\n ? String(formik.errors.expiryOption)\n : \"\";\n\n return (\n <SolidDialog open={open} onOpenChange={handleClose} style={{ maxWidth: 480 }} className=\"solid-api-key-dialog\">\n <SolidDialogHeader className=\"solid-api-key-dialog-header\">\n <SolidDialogTitle>Generate API Key</SolidDialogTitle>\n <SolidDialogDescription>\n Create a named key for this user. The raw key will be shown only once after generation.\n </SolidDialogDescription>\n </SolidDialogHeader>\n <SolidDialogSeparator />\n <SolidDialogBody className=\"solid-api-key-dialog-body\">\n <form id=\"generate-api-key-form\" onSubmit={formik.handleSubmit}>\n <div className=\"solid-api-key-dialog-fields\">\n <div className=\"solid-api-key-dialog-field\">\n <label htmlFor=\"api-key-name\" className=\"form-field-label\">\n Key Name <span style={{ color: \"var(--solid-danger-color, #ef4444)\" }}>*</span>\n </label>\n <SolidInput\n id=\"api-key-name\"\n name=\"name\"\n autoComplete=\"off\"\n placeholder=\"e.g. CI/CD pipeline, Mobile app\"\n value={formik.values.name}\n onChange={formik.handleChange}\n onBlur={formik.handleBlur}\n />\n {nameError && <SolidMessage severity=\"error\" text={nameError} />}\n </div>\n\n <div className=\"solid-api-key-dialog-field\">\n <label htmlFor=\"api-key-expiry\" className=\"form-field-label\">\n Expires\n </label>\n <SolidSelect\n value={formik.values.expiryOption}\n options={EXPIRY_OPTIONS}\n optionLabel=\"label\"\n optionValue=\"value\"\n onChange={({ value }) => {\n formik.setFieldValue(\"expiryOption\", value);\n if (value !== \"custom\") setCustomDate(null);\n }}\n native={false}\n />\n {formik.values.expiryOption === \"custom\" && (\n <SolidDatePicker\n selected={customDate}\n onChange={(date: Date | null) => setCustomDate(date)}\n minDate={new Date()}\n placeholderText=\"Select expiry date\"\n dateFormat=\"dd/MM/yyyy\"\n />\n )}\n {formik.values.expiryOption !== \"custom\" ? (\n <p className=\"solid-api-key-dialog-hint m-0\">Choose how long this key should remain valid.</p>\n ) : null}\n {expiryError && <SolidMessage severity=\"error\" text={expiryError} />}\n </div>\n </div>\n </form>\n </SolidDialogBody>\n <SolidDialogSeparator />\n <SolidDialogFooter className=\"solid-api-key-dialog-footer\">\n <SolidButton variant=\"outline\" type=\"button\" onClick={handleClose} disabled={isLoading}>\n Cancel\n </SolidButton>\n <SolidButton type=\"submit\" form=\"generate-api-key-form\" loading={isLoading}>\n Generate Key\n </SolidButton>\n </SolidDialogFooter>\n </SolidDialog>\n );\n}\n"]}
@@ -158,6 +158,7 @@ export function GenerateApiKeyModal({ open, onClose, onCreated, userId }: Genera
158
158
  formik.setFieldValue("expiryOption", value);
159
159
  if (value !== "custom") setCustomDate(null);
160
160
  }}
161
+ native={false}
161
162
  />
162
163
  {formik.values.expiryOption === "custom" && (
163
164
  <SolidDatePicker