@solidxai/core-ui 0.1.13-beta.3 → 0.1.13-beta.5

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 (162) 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 +75 -51
  32. package/dist/components/core/card/SolidCardView.js.map +1 -1
  33. package/dist/components/core/card/SolidCardView.tsx +61 -35
  34. package/dist/components/core/card/SolidCardViewConfigure.d.ts.map +1 -1
  35. package/dist/components/core/card/SolidCardViewConfigure.js +3 -7
  36. package/dist/components/core/card/SolidCardViewConfigure.js.map +1 -1
  37. package/dist/components/core/card/SolidCardViewConfigure.tsx +3 -9
  38. package/dist/components/core/common/SolidAccountSettings/SolidAccountSettings.module.css +99 -0
  39. package/dist/components/core/common/SolidAccountSettings/SolidVersionInfo.d.ts.map +1 -1
  40. package/dist/components/core/common/SolidAccountSettings/SolidVersionInfo.js +76 -5
  41. package/dist/components/core/common/SolidAccountSettings/SolidVersionInfo.js.map +1 -1
  42. package/dist/components/core/common/SolidAccountSettings/SolidVersionInfo.tsx +39 -4
  43. package/dist/components/core/common/SolidGlobalSearchElement.d.ts.map +1 -1
  44. package/dist/components/core/common/SolidGlobalSearchElement.js +172 -124
  45. package/dist/components/core/common/SolidGlobalSearchElement.js.map +1 -1
  46. package/dist/components/core/common/SolidGlobalSearchElement.tsx +73 -84
  47. package/dist/components/core/common/globalSearchPersistence.d.ts +3 -0
  48. package/dist/components/core/common/globalSearchPersistence.d.ts.map +1 -1
  49. package/dist/components/core/common/globalSearchPersistence.js +33 -24
  50. package/dist/components/core/common/globalSearchPersistence.js.map +1 -1
  51. package/dist/components/core/common/globalSearchPersistence.ts +34 -27
  52. package/dist/components/core/filter/fields/SolidMediaMultipleField.js +2 -2
  53. package/dist/components/core/filter/fields/SolidMediaMultipleField.js.map +1 -1
  54. package/dist/components/core/filter/fields/SolidMediaMultipleField.tsx +2 -2
  55. package/dist/components/core/filter/fields/SolidMediaSingleField.js +2 -2
  56. package/dist/components/core/filter/fields/SolidMediaSingleField.js.map +1 -1
  57. package/dist/components/core/filter/fields/SolidMediaSingleField.tsx +2 -2
  58. package/dist/components/core/filter/fields/SolidShortTextField.js +2 -2
  59. package/dist/components/core/filter/fields/SolidShortTextField.js.map +1 -1
  60. package/dist/components/core/filter/fields/SolidShortTextField.tsx +2 -2
  61. package/dist/components/core/form/SolidFormView.d.ts.map +1 -1
  62. package/dist/components/core/form/SolidFormView.js +2 -10
  63. package/dist/components/core/form/SolidFormView.js.map +1 -1
  64. package/dist/components/core/form/SolidFormView.tsx +2 -10
  65. package/dist/components/core/kanban/KanbanCard.d.ts.map +1 -1
  66. package/dist/components/core/kanban/KanbanCard.js +1 -1
  67. package/dist/components/core/kanban/KanbanCard.js.map +1 -1
  68. package/dist/components/core/kanban/KanbanCard.tsx +18 -16
  69. package/dist/components/core/kanban/SolidKanbanView.d.ts.map +1 -1
  70. package/dist/components/core/kanban/SolidKanbanView.js +73 -59
  71. package/dist/components/core/kanban/SolidKanbanView.js.map +1 -1
  72. package/dist/components/core/kanban/SolidKanbanView.tsx +57 -22
  73. package/dist/components/core/kanban/SolidKanbanViewConfigure.d.ts +1 -1
  74. package/dist/components/core/kanban/SolidKanbanViewConfigure.d.ts.map +1 -1
  75. package/dist/components/core/kanban/SolidKanbanViewConfigure.js +14 -9
  76. package/dist/components/core/kanban/SolidKanbanViewConfigure.js.map +1 -1
  77. package/dist/components/core/kanban/SolidKanbanViewConfigure.tsx +38 -9
  78. package/dist/components/core/list/SolidEmptyListViewPlaceholder.d.ts.map +1 -1
  79. package/dist/components/core/list/SolidEmptyListViewPlaceholder.js +2 -3
  80. package/dist/components/core/list/SolidEmptyListViewPlaceholder.js.map +1 -1
  81. package/dist/components/core/list/SolidEmptyListViewPlaceholder.tsx +2 -3
  82. package/dist/components/core/list/SolidListView.d.ts.map +1 -1
  83. package/dist/components/core/list/SolidListView.js +8 -16
  84. package/dist/components/core/list/SolidListView.js.map +1 -1
  85. package/dist/components/core/list/SolidListView.tsx +9 -15
  86. package/dist/components/core/list/SolidListViewConfigure.d.ts.map +1 -1
  87. package/dist/components/core/list/SolidListViewConfigure.js +22 -16
  88. package/dist/components/core/list/SolidListViewConfigure.js.map +1 -1
  89. package/dist/components/core/list/SolidListViewConfigure.tsx +32 -11
  90. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.d.ts +1 -1
  91. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.d.ts.map +1 -1
  92. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.js +3 -1
  93. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.js.map +1 -1
  94. package/dist/components/core/list/SolidListViewHeaderContextMenuButton.tsx +3 -1
  95. package/dist/components/core/list/columns/SolidMediaMultipleColumn.d.ts.map +1 -1
  96. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js +7 -0
  97. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js.map +1 -1
  98. package/dist/components/core/list/columns/SolidMediaMultipleColumn.tsx +5 -0
  99. package/dist/components/core/list/columns/SolidMediaSingleColumn.d.ts.map +1 -1
  100. package/dist/components/core/list/columns/SolidMediaSingleColumn.js +3 -0
  101. package/dist/components/core/list/columns/SolidMediaSingleColumn.js.map +1 -1
  102. package/dist/components/core/list/columns/SolidMediaSingleColumn.tsx +2 -0
  103. package/dist/components/core/list/widgets/SolidShortTextFieldImageRenderModeWidget.d.ts.map +1 -1
  104. package/dist/components/core/list/widgets/SolidShortTextFieldImageRenderModeWidget.js +3 -0
  105. package/dist/components/core/list/widgets/SolidShortTextFieldImageRenderModeWidget.js.map +1 -1
  106. package/dist/components/core/list/widgets/SolidShortTextFieldImageRenderModeWidget.tsx +2 -0
  107. package/dist/components/core/model/CreateModel.css +10 -0
  108. package/dist/components/core/model/FieldMetaDataForm.css +10 -0
  109. package/dist/components/core/model/FieldMetaDataForm.js +8 -8
  110. package/dist/components/core/model/FieldMetaDataForm.js.map +1 -1
  111. package/dist/components/core/model/FieldMetaDataForm.tsx +13 -13
  112. package/dist/components/core/module/ModuleMetadataExplorer.css +23 -0
  113. package/dist/components/core/module/ModuleMetadataExplorer.d.ts.map +1 -1
  114. package/dist/components/core/module/ModuleMetadataExplorer.js +1 -1
  115. package/dist/components/core/module/ModuleMetadataExplorer.js.map +1 -1
  116. package/dist/components/core/module/ModuleMetadataExplorer.tsx +9 -0
  117. package/dist/components/core/tree/SolidTreeView.d.ts.map +1 -1
  118. package/dist/components/core/tree/SolidTreeView.js +251 -152
  119. package/dist/components/core/tree/SolidTreeView.js.map +1 -1
  120. package/dist/components/core/tree/SolidTreeView.tsx +222 -106
  121. package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.d.ts.map +1 -1
  122. package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.js +1 -1
  123. package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.js.map +1 -1
  124. package/dist/components/core/users/ApiKeysTab/GenerateApiKeyModal.tsx +1 -0
  125. package/dist/components/layout/AdminHeaderActions.js +6 -13
  126. package/dist/components/layout/AdminHeaderActions.js.map +1 -1
  127. package/dist/components/layout/AdminHeaderActions.tsx +11 -11
  128. package/dist/components/layout/AdminLayout.d.ts.map +1 -1
  129. package/dist/components/layout/AdminLayout.js +2 -2
  130. package/dist/components/layout/AdminLayout.js.map +1 -1
  131. package/dist/components/layout/AdminLayout.tsx +5 -4
  132. package/dist/components/layout/AppSidebar.d.ts.map +1 -1
  133. package/dist/components/layout/AppSidebar.js +17 -2
  134. package/dist/components/layout/AppSidebar.js.map +1 -1
  135. package/dist/components/layout/AppSidebar.tsx +21 -2
  136. package/dist/components/shad-cn-ui/SolidDatePicker.d.ts +1 -1
  137. package/dist/components/shad-cn-ui/SolidDatePicker.d.ts.map +1 -1
  138. package/dist/components/shad-cn-ui/SolidDatePicker.js +35 -3
  139. package/dist/components/shad-cn-ui/SolidDatePicker.js.map +1 -1
  140. package/dist/components/shad-cn-ui/SolidDatePicker.tsx +84 -0
  141. package/dist/components/shad-cn-ui/SolidTooltip.d.ts.map +1 -1
  142. package/dist/components/shad-cn-ui/SolidTooltip.js +55 -2
  143. package/dist/components/shad-cn-ui/SolidTooltip.js.map +1 -1
  144. package/dist/components/shad-cn-ui/SolidTooltip.tsx +95 -1
  145. package/dist/helpers/permissions.d.ts +13 -0
  146. package/dist/helpers/permissions.d.ts.map +1 -1
  147. package/dist/helpers/permissions.js +79 -0
  148. package/dist/helpers/permissions.js.map +1 -1
  149. package/dist/helpers/permissions.ts +92 -1
  150. package/dist/resources/globals.css +109 -1
  151. package/dist/resources/solid-custom.css +53 -3
  152. package/dist/resources/solid-tailwind-generated.css +1 -1
  153. package/dist/routes/pages/admin/core/DashboardPage.d.ts.map +1 -1
  154. package/dist/routes/pages/admin/core/DashboardPage.js +2 -2
  155. package/dist/routes/pages/admin/core/DashboardPage.js.map +1 -1
  156. package/dist/routes/pages/admin/core/DashboardPage.tsx +2 -0
  157. package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.d.ts.map +1 -1
  158. package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.js +1 -1
  159. package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.js.map +1 -1
  160. package/dist/routes/pages/admin/core/DatasourceIntrospectionPage.tsx +1 -0
  161. package/dist/types/css-modules.d.ts +2 -0
  162. package/package.json +1 -1
@@ -14,7 +14,7 @@ import { useGetSolidViewLayoutQuery } from "../../../redux/api/solidViewApi";
14
14
  import qs from "qs";
15
15
  import { useSearchParams } from "../../../hooks/useSearchParams";
16
16
  import { SolidGlobalSearchElement } from "../common/SolidGlobalSearchElement";
17
- import { permissionExpression } from "../../../helpers/permissions";
17
+ import { getCollectionViewPermissionNames, permissionExpression } from "../../../helpers/permissions";
18
18
  import { SolidCreateButton } from "../common/SolidCreateButton";
19
19
  import { createSolidEntityApi } from "../../../redux/api/solidEntityApi";
20
20
  import { AggregationRule, GroupingRule } from "../common/GroupingComponent";
@@ -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
 
@@ -282,19 +308,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
282
308
  useEffect(() => {
283
309
  const fetchPermissions = async () => {
284
310
  if (params.modelName) {
285
- const permissionNames = [
286
- permissionExpression(params.modelName, 'create'),
287
- permissionExpression(params.modelName, 'delete'),
288
- permissionExpression(params.modelName, 'update'),
289
- permissionExpression(params.modelName, 'deleteMany'),
290
- permissionExpression(params.modelName, 'findOne'),
291
- permissionExpression(params.modelName, 'findMany'),
292
- permissionExpression(params.modelName, 'insertMany'),
293
- permissionExpression('importTransaction', 'create'),
294
- permissionExpression('exportTransaction', 'create'),
295
- permissionExpression('userViewMetadata', 'create'),
296
- permissionExpression('savedFilters', 'create')
297
- ];
311
+ const permissionNames = getCollectionViewPermissionNames(params.modelName);
298
312
  const queryData = {
299
313
  permissionNames: permissionNames,
300
314
  };
@@ -616,6 +630,18 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
616
630
  groupPath
617
631
  .map((item) => buildImplicitFilterCondition(item));
618
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
+
619
645
  const mergeFiltersWithImplicit = (implicitFilters: any[]) => {
620
646
  const baseFilters = latestFiltersRef.current;
621
647
  const hasBaseFilters = baseFilters && Object.keys(baseFilters).length > 0;
@@ -771,7 +797,8 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
771
797
 
772
798
  const queryString = qs.stringify(queryData, { encodeValuesOnly: true });
773
799
 
774
- const response = await triggerGetSolidEntities(queryString).unwrap();
800
+ const request = triggerGetSolidEntities(queryString);
801
+ const response = await request.unwrap().finally(() => request.unsubscribe());
775
802
  return { queryData, queryString, response };
776
803
  };
777
804
 
@@ -837,7 +864,8 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
837
864
 
838
865
 
839
866
 
840
- const response = await triggerGetSolidEntities(queryString).unwrap();
867
+ const request = triggerGetSolidEntities(queryString);
868
+ const response = await request.unwrap().finally(() => request.unsubscribe());
841
869
  return { queryData, queryString, response };
842
870
  };
843
871
 
@@ -847,7 +875,6 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
847
875
  groupMetaRows: any[],
848
876
  ruleIndex: number,
849
877
  parentPath: GroupPathItem[],
850
- parentKey: string
851
878
  ): TreeNode[] => {
852
879
  const rule = activeGroupingRules[ruleIndex];
853
880
  if (!rule?.fieldName) return [];
@@ -856,7 +883,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
856
883
  const filterField = getResolvedGroupField(fieldName);
857
884
  // const dateGranularity = dateTimeImplicitFilter(rule);
858
885
  const dateGranularity = getDateGranularity(rule);
859
- return (groupMetaRows || []).map((groupMeta, index) => {
886
+ return (groupMetaRows || []).map((groupMeta) => {
860
887
  const rawGroupLabel = groupMeta?.groupName;
861
888
  const normalizedGroupLabel =
862
889
  rawGroupLabel === "[object Object]"
@@ -872,7 +899,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
872
899
  ];
873
900
 
874
901
  return {
875
- key: `${parentKey}-g-${ruleIndex}-${index}`,
902
+ key: getGroupNodeKey(groupPath),
876
903
  data: {
877
904
  __treeMeta: {
878
905
  nodeType: "group",
@@ -927,12 +954,60 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
927
954
  });
928
955
  };
929
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
+
930
1004
  // ─── Root group load / paginate ───────────────────────────────────────────
931
1005
 
932
- const loadRootGroups = async (offset = 0) => {
1006
+ const loadRootGroups = async (offset = 0, affectedGroupPaths: GroupPathItem[][] = []) => {
933
1007
  if (!solidTreeViewMetaData || activeGroupingRules.length === 0) {
934
1008
  setTreeNodes([]);
935
1009
  setExpandedKeys({});
1010
+ setPaginationMap({});
936
1011
 
937
1012
  const queryObject = getFilterObjectFromLocalStorage();
938
1013
  if (queryObject) {
@@ -948,13 +1023,46 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
948
1023
  setTreeLoading(true);
949
1024
  try {
950
1025
  const { response } = await runGroupedQuery(0, [], offset, limit);
951
- const rootNodes = buildGroupNodes(response?.groupMeta || [], 0, [], "root");
952
- setTreeNodes(rootNodes);
953
- setExpandedKeys({});
954
- setSelectedNodeKeys({});
955
- // Collapse expanded keys since data changed
1026
+ const rootNodes = buildGroupNodes(response?.groupMeta || [], 0, []);
956
1027
  const total = response?.meta.totalRecords ?? 0;
957
- 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({});
958
1066
 
959
1067
  if (latestFilterPredicatesRef.current && latestFilterPredicatesRef.current.persistFilter) {
960
1068
  let queryData: any = {
@@ -978,7 +1086,6 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
978
1086
  }
979
1087
 
980
1088
  } catch (error: any) {
981
- setTreeNodes([]);
982
1089
  dispatch(showToast({ severity: "error", summary: "Failed to load tree", detail: error?.data?.message || error?.message || "Unable to load grouped data", life: 4000 }));
983
1090
  } finally {
984
1091
  setTreeLoading(false);
@@ -1002,33 +1109,10 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1002
1109
 
1003
1110
  const nodeKey = String(node.key);
1004
1111
  const limit = getPagination(nodeKey).limit || globalLimit || DEFAULT_PAGE_SIZE;
1005
- const nextRuleIndex = meta.ruleIndex + 1;
1006
1112
 
1007
1113
  setTreeLoading(true);
1008
1114
  try {
1009
- let children: TreeNode[] = [];
1010
- let total = 0;
1011
-
1012
- if (nextRuleIndex < activeGroupingRules.length) {
1013
-
1014
- const { response } = await runGroupedQuery(
1015
- nextRuleIndex,
1016
- meta.groupPath || [],
1017
- offset,
1018
- limit
1019
- );
1020
- children = buildGroupNodes(
1021
- response?.groupMeta || [],
1022
- nextRuleIndex,
1023
- meta.groupPath || [],
1024
- nodeKey
1025
- );
1026
- total = response?.meta.totalRecords ?? 0;
1027
- } else {
1028
- const { response } = await runLeafQuery(meta.groupPath || [], offset, limit);
1029
- children = buildRecordNodes(response?.records || [], nodeKey, meta.groupPath || []);
1030
- total = response?.meta.totalRecords ?? 0;
1031
- }
1115
+ const { children, total } = await fetchNodeChildren(node, offset, limit);
1032
1116
 
1033
1117
  setTreeNodes((prev) => updateNodeChildren(prev, nodeKey, children));
1034
1118
  setPagination(node.key as string, { offset, total: total, limit });
@@ -1225,6 +1309,9 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1225
1309
  // handle bulk deletion
1226
1310
  const deleteBulk = async () => {
1227
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));
1228
1315
  if (deleteList.length === 0) {
1229
1316
  setDeleteRecordsDialogVisible(false);
1230
1317
  return;
@@ -1234,7 +1321,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1234
1321
  await deleteManySolidEntities(deleteList).unwrap();
1235
1322
  dispatch(showToast({ severity: 'success', summary: 'Deleted', detail: ERROR_MESSAGES.RECORD_DELETE, life: 3000 }));
1236
1323
  setDeleteRecordsDialogVisible(false);
1237
- await loadRootGroups(getPagination("root").offset);
1324
+ await loadRootGroups(getPagination("root").offset, affectedGroupPaths);
1238
1325
  } catch (error: any) {
1239
1326
  dispatch(showToast({ severity: 'error', summary: 'Delete Failed', detail: error?.data?.message, life: 4000 }));
1240
1327
  }
@@ -1250,6 +1337,9 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1250
1337
 
1251
1338
  const recoverAll = async () => {
1252
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));
1253
1343
  if (recoverList.length === 0) {
1254
1344
  setRecoverDialogVisible(false);
1255
1345
  return;
@@ -1264,7 +1354,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1264
1354
  detail: response?.data?.message || "Records recovered successfully.",
1265
1355
  life: 3000,
1266
1356
  }));
1267
- await loadRootGroups(getPagination("root").offset);
1357
+ await loadRootGroups(getPagination("root").offset, affectedGroupPaths);
1268
1358
  } catch (error: any) {
1269
1359
  dispatch(showToast({
1270
1360
  severity: "error",
@@ -1304,8 +1394,8 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1304
1394
  solidListViewMetaData: solidTreeViewMetaData,
1305
1395
  fieldMetadata,
1306
1396
  column,
1307
- setLightboxUrls: () => { },
1308
- setOpenLightbox: () => { },
1397
+ setLightboxUrls,
1398
+ setOpenLightbox,
1309
1399
  });
1310
1400
 
1311
1401
  if (!React.isValidElement(listColumn)) return null;
@@ -1502,9 +1592,19 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1502
1592
 
1503
1593
 
1504
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
+ );
1505
1604
 
1506
1605
  const [selectedSolidViewData, setSelectedSolidViewData] = useState<any>();
1507
1606
  const [deleteEntity, setDeleteEntity] = useState(false);
1607
+ const entityDisplayName = solidTreeViewMetaData?.data?.solidView?.model?.displayName || params?.modelName;
1508
1608
  const openRowForEdit = (rowData: any, viewMode: "edit" | "view") => {
1509
1609
  storeCurrentModelViewContext();
1510
1610
 
@@ -1549,13 +1649,16 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1549
1649
  throw new Error(ERROR_MESSAGES.NO_ENTITY_SELECTED);
1550
1650
  }
1551
1651
 
1652
+ const affectedGroupPath = selectedSolidViewData?.__treeMeta?.groupPath;
1552
1653
  const response: any = await deleteSolidSingleEntiry(selectedSolidViewData.id);
1553
-
1554
1654
  if (response?.data?.statusCode === 200) {
1555
1655
  setDeleteEntity(false);
1556
1656
  setSelectedSolidViewData(undefined);
1557
1657
  dispatch(showToast({ severity: "success", summary: ERROR_MESSAGES.DELETED, detail: ERROR_MESSAGES.ENTITY_DELETE, life: 3000 }));
1558
- await loadRootGroups(getPagination("root").offset);
1658
+ await loadRootGroups(
1659
+ getPagination("root").offset,
1660
+ Array.isArray(affectedGroupPath) ? [affectedGroupPath] : [],
1661
+ );
1559
1662
  } else {
1560
1663
  dispatch(showToast({ severity: "error", summary: ERROR_MESSAGES.DELETE_FAIELD, detail: response?.error?.data?.error, sticky: true }));
1561
1664
  }
@@ -1575,7 +1678,10 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1575
1678
  detail: response?.data?.message || "Record recovered successfully.",
1576
1679
  life: 3000,
1577
1680
  }));
1578
- await loadRootGroups(getPagination("root").offset);
1681
+ await loadRootGroups(
1682
+ getPagination("root").offset,
1683
+ Array.isArray(record?.__treeMeta?.groupPath) ? [record.__treeMeta.groupPath] : [],
1684
+ );
1579
1685
  } catch (error: any) {
1580
1686
  dispatch(showToast({
1581
1687
  severity: "error",
@@ -1780,6 +1886,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1780
1886
  ref={solidGlobalSearchElementRef}
1781
1887
  viewData={solidTreeViewMetaData}
1782
1888
  handleApplyCustomFilter={handleApplyCustomFilter}
1889
+ filterPredicates={filterPredicates}
1783
1890
  />
1784
1891
  </div>
1785
1892
  )}
@@ -1801,6 +1908,22 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1801
1908
  </div>
1802
1909
  )}
1803
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
+
1804
1927
  {actionsAllowed.includes(`${permissionExpression(params.modelName, "create")}`) &&
1805
1928
  solidTreeViewMetaData?.data?.solidView?.layout?.attrs.create !== false && (
1806
1929
  <SolidCreateButton
@@ -1870,7 +1993,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1870
1993
  </div>
1871
1994
 
1872
1995
  {solidTreeViewMetaData?.data?.solidView?.layout?.attrs.enableGlobalSearch === true &&
1873
- showGlobalSearchElement && (
1996
+ shouldShowMobileSearchElement && (
1874
1997
  <div className="flex lg:hidden">
1875
1998
  <SolidGlobalSearchElement
1876
1999
  viewType="tree"
@@ -1879,6 +2002,7 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
1879
2002
  ref={solidGlobalSearchElementRef}
1880
2003
  viewData={solidTreeViewMetaData}
1881
2004
  handleApplyCustomFilter={handleApplyCustomFilter}
2005
+ filterPredicates={filterPredicates}
1882
2006
  />
1883
2007
  </div>
1884
2008
  )}
@@ -2062,53 +2186,37 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
2062
2186
  </div>
2063
2187
  </div>
2064
2188
 
2065
- <SolidDialog
2189
+ <SolidConfirmDialog
2066
2190
  open={deleteEntity}
2067
- onOpenChange={(open) => {
2068
- if (!open) setDeleteEntity(false);
2069
- }}
2070
- contentClassName="solid-confirm-dialog solid-tree-confirm-dialog"
2071
- showHeader={false}
2072
- >
2073
- <SolidDialogHeader>
2074
- <SolidDialogTitle>
2075
- Delete {solidTreeViewMetaData?.data?.solidView?.model?.displayName || params?.modelName}
2076
- </SolidDialogTitle>
2077
- </SolidDialogHeader>
2078
- <SolidDialogSeparator />
2079
- <SolidDialogBody>
2080
- <SolidDialogDescription className="m-0">
2081
- {`Are you sure you want to delete this ${solidTreeViewMetaData?.data?.solidView?.model?.displayName || params?.modelName}?`}
2082
- </SolidDialogDescription>
2083
- </SolidDialogBody>
2084
- <SolidDialogFooter>
2085
- <SolidButton label="Cancel" size="sm" variant="outline" onClick={() => setDeleteEntity(false)} />
2086
- <SolidButton label="Delete" size="sm" severity="danger" onClick={handleDeleteEntity} />
2087
- </SolidDialogFooter>
2088
- </SolidDialog>
2089
-
2090
- <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
2091
2206
  open={isDeleteRecordsDialogVisible}
2092
- onOpenChange={(open) => {
2093
- if (!open) setDeleteRecordsDialogVisible(false);
2094
- }}
2095
- contentClassName="solid-confirm-dialog solid-tree-confirm-dialog"
2096
- showHeader={false}
2097
- >
2098
- <SolidDialogHeader>
2099
- <SolidDialogTitle>Confirm Delete</SolidDialogTitle>
2100
- </SolidDialogHeader>
2101
- <SolidDialogSeparator />
2102
- <SolidDialogBody>
2103
- <SolidDialogDescription className="m-0">
2104
- Are you sure you want to delete the selected records?
2105
- </SolidDialogDescription>
2106
- </SolidDialogBody>
2107
- <SolidDialogFooter>
2108
- <SolidButton label="Cancel" size="sm" variant="outline" onClick={onDeleteClose} />
2109
- <SolidButton label="Delete" size="sm" severity="danger" onClick={deleteBulk} />
2110
- </SolidDialogFooter>
2111
- </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
+ />
2112
2220
 
2113
2221
  <SolidDialog
2114
2222
  open={isRecoverDialogVisible}
@@ -2133,6 +2241,14 @@ export const SolidTreeView = forwardRef<SolidTreeViewHandle, SolidTreeViewParams
2133
2241
  </SolidDialogFooter>
2134
2242
  </SolidDialog>
2135
2243
 
2244
+ {openLightbox && (
2245
+ <SolidLightbox
2246
+ open={openLightbox}
2247
+ slides={lightboxSlides}
2248
+ onClose={() => setOpenLightbox(false)}
2249
+ />
2250
+ )}
2251
+
2136
2252
  </div>
2137
2253
  );
2138
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":"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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 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