Skip to content

Commit 12f2d45

Browse files
Merge pull request #1877 from iamfaran/fix/1848-optimization
[Fix]: #1848 remove unnecessary calls and optimization
2 parents 67643f6 + 2df0a8f commit 12f2d45

File tree

12 files changed

+175
-95
lines changed

12 files changed

+175
-95
lines changed

client/packages/lowcoder/src/api/datasourceApi.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ export class DatasourceApi extends Api {
187187
return Api.get(DatasourceApi.url + `/listByOrg?orgId=${orgId}`, {...res});
188188
}
189189

190+
static getDatasourceById(id: string): AxiosPromise<GenericApiResponse<Datasource>> {
191+
return Api.get(`${DatasourceApi.url}/${id}`);
192+
}
193+
190194
static createDatasource(
191195
datasourceConfig: Partial<Datasource>
192196
): AxiosPromise<GenericApiResponse<Datasource>> {

client/packages/lowcoder/src/pages/ApplicationV2/FolderView.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { Helmet } from "react-helmet";
99
import { trans } from "i18n";
1010
import {ApplicationPaginationType} from "@lowcoder-ee/util/pagination/type";
1111
import {fetchFolderElements} from "@lowcoder-ee/util/pagination/axios";
12+
import { fetchFolderElements as fetchFolderElementsRedux } from "../../redux/reduxActions/folderActions";
13+
import { getUser } from "../../redux/selectors/usersSelectors";
1214

1315
function getBreadcrumbs(
1416
folder: FolderMeta,
@@ -52,6 +54,7 @@ export function FolderView() {
5254

5355
const element = useSelector(folderElementsSelector);
5456
const allFolders = useSelector(foldersSelector);
57+
const user = useSelector(getUser);
5558

5659
const folder = allFolders.filter((f) => f.folderId === folderId)[0] || {};
5760
const breadcrumbs = getBreadcrumbs(folder, allFolders, [
@@ -61,6 +64,13 @@ export function FolderView() {
6164
},
6265
]);
6366

67+
// Fetch folder data for breadcrumbs if not available
68+
useEffect(() => {
69+
if (allFolders.length === 0 && user.currentOrgId) {
70+
dispatch(fetchFolderElementsRedux({}));
71+
}
72+
}, [allFolders.length, user.currentOrgId, dispatch]);
73+
6474
useEffect( () => {
6575
try{
6676
fetchFolderElements({

client/packages/lowcoder/src/pages/ApplicationV2/HomeResOptions.tsx

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { HomeResTypeEnum } from "../../types/homeRes";
33
import { exportApplicationAsJSONFile } from "./components/AppImport";
44
import { CustomModal, EditPopover, EditPopoverItemType, PointIcon } from "lowcoder-design";
55
import { HomeResInfo } from "../../util/homeResUtils";
6-
import { recycleApplication } from "../../redux/reduxActions/applicationActions";
7-
import { deleteFolder } from "../../redux/reduxActions/folderActions";
86
import { useDispatch } from "react-redux";
97
import React, { useState } from "react";
108
import styled from "styled-components";
@@ -13,6 +11,9 @@ import { useParams } from "react-router-dom";
1311
import { AppTypeEnum } from "constants/applicationConstants";
1412
import { CopyModal } from "pages/common/copyModal";
1513
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
14+
import ApplicationApi from "../../api/applicationApi";
15+
import { FolderApi } from "../../api/folderApi";
16+
import { ReduxActionTypes } from "constants/reduxActionConstants";
1617

1718
const PopoverIcon = styled(PointIcon)`
1819
cursor: pointer;
@@ -80,23 +81,20 @@ export const HomeResOptions = (props: {
8081
type: HomeResInfo[res.type].name,
8182
name: <b>{res.name}</b>,
8283
}),
83-
onConfirm: () =>{
84-
new Promise((resolve, reject) => {
85-
dispatch(
86-
recycleApplication(
87-
{ applicationId: res.id, folderId: folderId },
88-
() => {
89-
messageInstance.success(trans("success"));
90-
resolve(true);
91-
},
92-
() => reject()
93-
)
94-
);
84+
onConfirm: async () => {
85+
try {
86+
await ApplicationApi.recycleApplication({
87+
applicationId: res.id,
88+
folderId: folderId || ""
89+
});
90+
messageInstance.success(trans("success"));
9591
setTimeout(() => {
9692
setModify(!modify);
9793
}, 200);
98-
})
99-
94+
} catch (error) {
95+
console.error("Failed to recycle application:", error);
96+
messageInstance.error("Failed to delete application");
97+
}
10098
},
10199
confirmBtnType: "delete",
102100
okText: trans("home.moveToTrash"),
@@ -122,22 +120,27 @@ export const HomeResOptions = (props: {
122120
type: HomeResInfo[res.type].name.toLowerCase(),
123121
name: <b>{res.name}</b>,
124122
}),
125-
onConfirm: () =>{
126-
new Promise((resolve, reject) => {
127-
dispatch(
128-
deleteFolder(
129-
{ folderId: res.id, parentFolderId: folderId },
130-
() => {
131-
messageInstance.success(trans("home.deleteSuccessMsg"));
132-
resolve(true);
133-
},
134-
() => reject()
135-
)
136-
);
137-
})
138-
setTimeout(() => {
139-
setModify(!modify);
140-
}, 200);
123+
onConfirm: async () => {
124+
try {
125+
await FolderApi.deleteFolder({
126+
folderId: res.id,
127+
parentFolderId: folderId || ""
128+
});
129+
130+
// Update Redux state to remove deleted folder from dropdown
131+
dispatch({
132+
type: ReduxActionTypes.DELETE_FOLDER_SUCCESS,
133+
payload: { folderId: res.id, parentFolderId: folderId || "" }
134+
});
135+
136+
messageInstance.success(trans("home.deleteSuccessMsg"));
137+
setTimeout(() => {
138+
setModify(!modify);
139+
}, 200);
140+
} catch (error) {
141+
console.error("Failed to delete folder:", error);
142+
messageInstance.error("Failed to delete folder");
143+
}
141144
},
142145
confirmBtnType: "delete",
143146
okText: trans("delete"),

client/packages/lowcoder/src/pages/ApplicationV2/TrashTableView.tsx

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import styled from "styled-components";
55
import { useDispatch } from "react-redux";
66
import { HomeResInfo } from "../../util/homeResUtils";
77
import { HomeResTypeEnum } from "../../types/homeRes";
8-
import { deleteApplication, restoreApplication } from "../../redux/reduxActions/applicationActions";
98
import { HomeRes } from "./HomeLayout";
109
import { trans, transToNode } from "../../i18n";
1110
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
1211
import { BrandedIcon } from "@lowcoder-ee/components/BrandedIcon";
12+
import ApplicationApi from "../../api/applicationApi";
1313

1414
const OperationWrapper = styled.div`
1515
display: flex;
@@ -123,17 +123,18 @@ export const TrashTableView = (props: { resources: HomeRes[] , setModify: any, m
123123
style={{ padding: "0 8px", width: "fit-content", minWidth: "52px" }}
124124
buttonType={"blue"}
125125
className={"home-datasource-edit-button"}
126-
onClick={() =>{
127-
dispatch(
128-
restoreApplication({ applicationId: item.id }, () => {
129-
messageInstance.success(trans("home.recoverSuccessMsg"));
130-
})
131-
)
126+
onClick={async () => {
127+
try {
128+
await ApplicationApi.restoreApplication({ applicationId: item.id });
129+
messageInstance.success(trans("home.recoverSuccessMsg"));
132130
setTimeout(() => {
133-
setModify(!modify);
131+
setModify(!modify);
134132
}, 200);
135-
}
136-
}
133+
} catch (error) {
134+
console.error("Failed to restore application:", error);
135+
messageInstance.error("Failed to restore application");
136+
}
137+
}}
137138
>
138139
{trans("recover")}
139140
</EditBtn>
@@ -148,27 +149,21 @@ export const TrashTableView = (props: { resources: HomeRes[] , setModify: any, m
148149
type: HomeResInfo[item.type].name.toLowerCase(),
149150
name: <b>{item.name}</b>,
150151
}),
151-
onConfirm: () =>{
152-
new Promise((resolve, reject) => {
153-
dispatch(
154-
deleteApplication(
155-
{ applicationId: item.id },
156-
() => {
157-
messageInstance.success(trans("home.deleteSuccessMsg"));
158-
resolve(true);
159-
},
160-
() => reject()
161-
)
162-
);
163-
})
152+
onConfirm: async () => {
153+
try {
154+
await ApplicationApi.deleteApplication({ applicationId: item.id });
155+
messageInstance.success(trans("home.deleteSuccessMsg"));
164156
setTimeout(() => {
165-
setModify(!modify);
157+
setModify(!modify);
166158
}, 200);
159+
} catch (error) {
160+
console.error("Failed to delete application:", error);
161+
messageInstance.error("Failed to delete application permanently");
162+
}
167163
},
168164
confirmBtnType: "delete",
169165
okText: trans("delete"),
170166
})
171-
172167
}
173168
style={{ marginLeft: "12px", width: "76px" }}
174169
>

client/packages/lowcoder/src/pages/ApplicationV2/index.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,6 @@ export default function ApplicationHome() {
124124
setIsPreloadCompleted(true);
125125
}, [org, orgHomeId]);
126126

127-
useEffect(() => {
128-
// Check if we need to fetch data (either no folders or no applications)
129-
if (allFoldersCount !== 0 && allAppCount !== 0) {
130-
return;
131-
}
132-
133-
user.currentOrgId && dispatch(fetchFolderElements({}));
134-
}, [dispatch, allFoldersCount, allAppCount, user.currentOrgId]);
135-
136127
if (fetchingUser || !isPreloadCompleted) {
137128
return <ProductLoading />;
138129
}

client/packages/lowcoder/src/pages/datasource/datasourceEditPage.tsx

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
import styled from "styled-components";
22
import history from "../../util/history";
33
import { default as Button } from "antd/es/button";
4-
import { useCallback, useMemo, useState } from "react";
4+
import { Spin } from "antd";
5+
import { useCallback, useEffect, useMemo, useState } from "react";
56
import { CopyTextButton, DocIcon, PackUpIcon, TacoButton } from "lowcoder-design";
67
import { useDatasourceForm } from "./form/useDatasourceForm";
78
import { useParams } from "react-router-dom";
89
import { DATASOURCE_URL } from "../../constants/routesURL";
910
import { useSelector } from "react-redux";
10-
import { getDataSource, getDataSourceTypes } from "../../redux/selectors/datasourceSelectors";
11+
import { getDataSourceTypes } from "../../redux/selectors/datasourceSelectors";
1112
import { trans } from "i18n";
1213
import { DatasourceType } from "@lowcoder-ee/constants/queryConstants";
1314
import { getDatasourceTutorial } from "@lowcoder-ee/util/tutorialUtils";
1415
import { getDataSourceFormManifest } from "./getDataSourceFormManifest";
1516
import DataSourceIcon from "components/DataSourceIcon";
1617
import { Helmet } from "react-helmet";
17-
18+
import { DatasourceApi } from "@lowcoder-ee/api/datasourceApi";
19+
import { DatasourceInfo } from "@lowcoder-ee/api/datasourceApi";
20+
import { GenericApiResponse } from "../../api/apiResponses";
21+
import { Datasource } from "@lowcoder-ee/constants/datasourceConstants";
22+
import { AxiosResponse } from "axios";
1823
const Wrapper = styled.div`
1924
display: flex;
2025
justify-content: center;
@@ -154,16 +159,44 @@ type DatasourcePathParams = {
154159

155160
export const DatasourceEditPage = () => {
156161
const { datasourceId, datasourceType } = useParams<DatasourcePathParams>();
157-
const datasourceList = useSelector(getDataSource);
158162
const datasourceTypes = useSelector(getDataSourceTypes);
159163
const [isReady, setIsReady] = useState(true);
160164

161-
const datasourceInfo = useMemo(() => {
165+
166+
const [datasourceInfo, setDatasourceInfo] = useState<DatasourceInfo | undefined>();
167+
const [loading, setLoading] = useState(false);
168+
169+
// Fetch individual datasource when editing
170+
useEffect(() => {
162171
if (!datasourceId) {
163-
return undefined;
172+
setDatasourceInfo(undefined);
173+
return;
164174
}
165-
return datasourceList.find((info) => info.datasource.id === datasourceId);
166-
}, [datasourceId, datasourceList]);
175+
176+
const fetchDatasource = async () => {
177+
setLoading(true);
178+
try {
179+
const response: AxiosResponse<GenericApiResponse<Datasource>> = await DatasourceApi.getDatasourceById(datasourceId);
180+
if (response.data.success) {
181+
// Transform to DatasourceInfo format
182+
setDatasourceInfo({
183+
datasource: response.data.data,
184+
edit: true, // Assume editable since user reached edit page
185+
});
186+
} else {
187+
console.error('API returned error:', response.data);
188+
setDatasourceInfo(undefined);
189+
}
190+
} catch (error: any) {
191+
console.error('Failed to fetch datasource:', error);
192+
setDatasourceInfo(undefined);
193+
} finally {
194+
setLoading(false);
195+
}
196+
};
197+
198+
fetchDatasource();
199+
}, [datasourceId]);
167200

168201
const dataSourceTypeInfo = useMemo(() => {
169202
if (datasourceId) {
@@ -181,6 +214,26 @@ export const DatasourceEditPage = () => {
181214
setIsReady(isReady);
182215
}, []);
183216

217+
// Show loading state while fetching datasource
218+
if (loading) {
219+
return (
220+
<Wrapper>
221+
<ContentWrapper>
222+
<div style={{
223+
display: 'flex',
224+
justifyContent: 'center',
225+
alignItems: 'center',
226+
height: '400px',
227+
flexDirection: 'column',
228+
gap: '16px'
229+
}}>
230+
<Spin size="large" />
231+
</div>
232+
</ContentWrapper>
233+
</Wrapper>
234+
);
235+
}
236+
184237
if (!finalDataSourceType) {
185238
return null;
186239
}

client/packages/lowcoder/src/pages/datasource/datasourceList.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import styled from "styled-components";
22
import { EditPopover, PointIcon, Search, TacoButton } from "lowcoder-design";
3-
import React, {useEffect, useState} from "react";
3+
import {useEffect, useState} from "react";
44
import { useDispatch, useSelector } from "react-redux";
5-
import { getDataSource, getDataSourceLoading, getDataSourceTypesMap } from "../../redux/selectors/datasourceSelectors";
5+
import { getDataSourceTypesMap } from "../../redux/selectors/datasourceSelectors";
66
import { deleteDatasource } from "../../redux/reduxActions/datasourceActions";
77
import { isEmpty } from "lodash";
88
import history from "../../util/history";
@@ -113,7 +113,6 @@ export const DatasourceList = () => {
113113
const [modify, setModify] = useState(false);
114114
const currentUser = useSelector(getUser);
115115
const orgId = currentUser.currentOrgId;
116-
const datasourceLoading = useSelector(getDataSourceLoading);
117116
const plugins = useSelector(getDataSourceTypesMap);
118117
interface ElementsState {
119118
elements: DatasourceInfo[];
@@ -123,6 +122,7 @@ export const DatasourceList = () => {
123122
const [elements, setElements] = useState<ElementsState>({ elements: [], total: 0 });
124123
const [currentPage, setCurrentPage] = useState(1);
125124
const [pageSize, setPageSize] = useState(10);
125+
const [paginationLoading, setPaginationLoading] = useState(false);
126126

127127
useEffect(()=> {
128128
const timer = setTimeout(() => {
@@ -133,6 +133,7 @@ export const DatasourceList = () => {
133133
}, [searchValue])
134134

135135
useEffect( () => {
136+
setPaginationLoading(true);
136137
fetchDatasourcePagination(
137138
{
138139
orgId: orgId,
@@ -146,6 +147,8 @@ export const DatasourceList = () => {
146147
}
147148
else
148149
console.error("ERROR: fetchFolderElements", result.error)
150+
}).finally(() => {
151+
setPaginationLoading(false);
149152
})
150153
}, [currentPage, pageSize, searchValues, modify]
151154
)
@@ -195,7 +198,7 @@ export const DatasourceList = () => {
195198
<BodyWrapper>
196199
<StyledTable
197200
loading={{
198-
spinning: datasourceLoading,
201+
spinning: paginationLoading,
199202
indicator: <LoadingOutlined spin style={{ fontSize: 30 }} />
200203
}}
201204
rowClassName={(record: any) => (!record.edit ? "datasource-can-not-edit" : "")}

0 commit comments

Comments
 (0)