Skip to content

Implemented pagination in some APIs. #1351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 39 commits into from
Dec 4, 2024
Merged
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
16d8e1a
Added pagination ui
Imiss-U1025 Nov 14, 2024
b6d8a1b
Removed Your Folder Component
Imiss-U1025 Nov 26, 2024
efa56c3
Added utilities for pagination.
Imiss-U1025 Nov 26, 2024
a602426
Implemented pagination in Your App.
Imiss-U1025 Nov 26, 2024
fd62105
Implemented pagination in trash.
Imiss-U1025 Nov 27, 2024
3d9f56d
Imported types from util/pagination
Imiss-U1025 Nov 27, 2024
fa580fb
Added utilities for pagination.
Imiss-U1025 Nov 27, 2024
7bc6eb6
Implemented pagination in User Group List.
Imiss-U1025 Nov 27, 2024
41927a3
Implemented pagination in Data Sources.
Imiss-U1025 Nov 27, 2024
f2140e4
Implemented pagination in groupUsersPermission.
Imiss-U1025 Nov 27, 2024
8c82fd1
Implemented pagination in organizations's member.
Imiss-U1025 Nov 28, 2024
d65c2d5
Implemented pagination in Query Library and made fetchJsDatasourcePag…
Imiss-U1025 Nov 28, 2024
cce98cd
Implemented pagination in login.
Imiss-U1025 Nov 28, 2024
e2c1efc
Implemented realtime processing of create new folder in HomeView.
Imiss-U1025 Nov 28, 2024
3ce6469
Change search method in query library.
Imiss-U1025 Nov 28, 2024
a0a6fa1
Changed search method in Data Sources.
Imiss-U1025 Nov 28, 2024
b27f13f
Changed search method in marketplace.
Imiss-U1025 Nov 28, 2024
f51f681
Processed immediate activity in TrashView.
Imiss-U1025 Nov 29, 2024
cfe991d
Processed immediate activity in Your Apps.
Imiss-U1025 Nov 29, 2024
add7083
Processed immediate activity in Setting/UserGroup.
Imiss-U1025 Nov 29, 2024
a65c737
Added back button in your apps.
Imiss-U1025 Nov 29, 2024
1d41e80
Processed immediate activity in Query Library.
Imiss-U1025 Nov 29, 2024
4e1e0c0
Processed immediate activity in Data Sources.
Imiss-U1025 Nov 29, 2024
90648dc
Removed unnessary APIs.
Imiss-U1025 Nov 29, 2024
b332980
Fixed Search functions.
Imiss-U1025 Nov 29, 2024
34ce989
Fixed pagination of folders and UI.
Imiss-U1025 Nov 29, 2024
071e0ab
Fixed an issue that can not search Navigation.
Imiss-U1025 Nov 29, 2024
24469a3
Fixed loading indicator in setting/permission.
Imiss-U1025 Dec 1, 2024
56ff238
Fixed an issue that does not display 'Add member' button and 'Remove …
Imiss-U1025 Dec 2, 2024
346760c
Optimized called APIs and in User groups.
Imiss-U1025 Dec 2, 2024
3d75c3a
Fixed an issue that call double API in login.
Imiss-U1025 Dec 3, 2024
ffd2889
Does not show Pagination when No data in Data Source.
Imiss-U1025 Dec 3, 2024
32b3c3b
Implemented update-on-action when import file and create new Data Sou…
Imiss-U1025 Dec 3, 2024
bed5f55
Removed unnessary API calling (folders/elements) when first loading.
Imiss-U1025 Dec 3, 2024
6f9b821
Updating Yarn Lock
Dec 3, 2024
326d27e
Fixed an issue that app does not move to folder.
Imiss-U1025 Dec 3, 2024
9fcd924
Added categories dropdown button in Your apps.
Imiss-U1025 Dec 3, 2024
8886606
Fixed an Navigation issue in Trash and Your Apps.
Imiss-U1025 Dec 4, 2024
0364857
Merge branch 'dev' into feature-pagination
FalkWolsky Dec 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions client/packages/lowcoder-design/src/components/Search.tsx
Original file line number Diff line number Diff line change
@@ -62,24 +62,35 @@ interface ISearch {
placeholder: string;
value: string;
onChange: (value: React.ChangeEvent<HTMLInputElement>) => void;
onEnterPress?: (value: string) => void; // Added for capturing Enter key press
disabled?: boolean;
}

export const Search = (props: ISearch & InputProps) => {
const { value, onChange, style, disabled, placeholder, ...others } = props;
const { value, onChange, style, disabled, placeholder, onEnterPress, ...others } = props;

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
onChange && onChange(e);
};

// Handling Enter key press
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && onEnterPress) {
onEnterPress(value);
}
};

return (
<SearchDiv style={style}>
<SearchInput
disabled={disabled}
placeholder={placeholder}
onChange={handleChange}
value={value}
prefix={<SearchIcon />}
{...others}
/>
</SearchDiv>
<SearchDiv style={style}>
<SearchInput
disabled={disabled}
placeholder={placeholder}
onChange={handleChange}
onKeyDown={handleKeyDown} // Listening for key down events
value={value}
prefix={<SearchIcon />}
{...others}
/>
</SearchDiv>
);
};
};
7 changes: 6 additions & 1 deletion client/packages/lowcoder/src/api/applicationApi.ts
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ import {
SetAppEditingStatePayload,
UpdateAppPermissionPayload,
} from "redux/reduxActions/applicationActions";
import { ApiResponse, GenericApiResponse } from "./apiResponses";
import {ApiResponse, GenericApiResponse} from "./apiResponses";
import { JSONObject, JSONValue } from "util/jsonTypes";
import {
ApplicationDetail,
@@ -24,6 +24,7 @@ import {
} from "constants/applicationConstants";
import { CommonSettingResponseData } from "./commonSettingApi";
import { ResourceType } from "@lowcoder-ee/constants/queryConstants";
import {fetchAppRequestType, GenericApiPaginationResponse} from "@lowcoder-ee/util/pagination/type";

export interface HomeOrgMeta {
id: string;
@@ -108,6 +109,10 @@ class ApplicationApi extends Api {
return Api.get(ApplicationApi.newURLPrefix + "/list", { ...request, withContainerSize: false });
}

static fetchAllApplicationsPagination(request: fetchAppRequestType): AxiosPromise<GenericApiPaginationResponse<ApplicationMeta[]>> {
return Api.get(ApplicationApi.newURLPrefix + "/list", { ...request, withContainerSize: false, applicationStatus: "RECYCLED" });
}

static fetchAllModules(request: HomeDataPayload): AxiosPromise<ApplicationMeta[]> {
return Api.get(ApplicationApi.newURLPrefix + "/list", {
applicationType: AppTypeEnum.Module,
15 changes: 15 additions & 0 deletions client/packages/lowcoder/src/api/datasourceApi.ts
Original file line number Diff line number Diff line change
@@ -8,6 +8,11 @@ import { JSONArray } from "util/jsonTypes";
import { AuthType, HttpOAuthGrantType } from "pages/datasource/form/httpDatasourceForm";
import { Datasource } from "@lowcoder-ee/constants/datasourceConstants";
import { DataSourcePluginMeta } from "lowcoder-sdk/dataSource";
import {
fetchDataSourcePaginationRequestType,
fetchDBRequestType,
GenericApiPaginationResponse
} from "@lowcoder-ee/util/pagination/type";

export interface PreparedStatementConfig {
enableTurnOffPreparedStatement: boolean;
@@ -164,6 +169,11 @@ export class DatasourceApi extends Api {
return Api.get(DatasourceApi.url + `/jsDatasourcePlugins?appId=${appId}`);
}

static fetchJsDatasourcePaginationByApp( request: fetchDataSourcePaginationRequestType ): AxiosPromise<GenericApiPaginationResponse<NodePluginDatasourceInfo[]>> {
const {appId, ...res} = request
return Api.get(DatasourceApi.url + `/jsDatasourcePlugins?appId=${appId}` ,{...res});
}

static fetchDatasourceByApp(appId: string): AxiosPromise<GenericApiResponse<DatasourceInfo[]>> {
return Api.get(DatasourceApi.url + `/listByApp?appId=${appId}`);
}
@@ -172,6 +182,11 @@ export class DatasourceApi extends Api {
return Api.get(DatasourceApi.url + `/listByOrg?orgId=${orgId}`);
}

static fetchDatasourcePaginationByOrg(request: fetchDBRequestType): AxiosPromise<GenericApiPaginationResponse<DatasourceInfo[]>> {
const {orgId, ...res} = request;
return Api.get(DatasourceApi.url + `/listByOrg?orgId=${orgId}`, {...res});
}

static createDatasource(
datasourceConfig: Partial<Datasource>
): AxiosPromise<GenericApiResponse<Datasource>> {
11 changes: 11 additions & 0 deletions client/packages/lowcoder/src/api/folderApi.ts
Original file line number Diff line number Diff line change
@@ -9,6 +9,10 @@ import {
UpdateFolderPayload,
} from "../redux/reduxActions/folderActions";
import { ApplicationMeta, FolderMeta } from "../constants/applicationConstants";
import {
fetchFolderRequestType,
GenericApiPaginationResponse
} from "@lowcoder-ee/util/pagination/type";

export class FolderApi extends Api {
static url = "/folders";
@@ -40,4 +44,11 @@ export class FolderApi extends Api {
): AxiosPromise<GenericApiResponse<(ApplicationMeta | FolderMeta)[]>> {
return Api.get(FolderApi.url + `/elements`, { id: request.folderId });
}

static fetchFolderElementsPagination(
request: fetchFolderRequestType
): AxiosPromise<GenericApiPaginationResponse<(ApplicationMeta | FolderMeta)[]>> {
const {id, ...res} = request
return request.id ? Api.get(FolderApi.url + `/elements`,{id: id, ...res}) : Api.get(FolderApi.url + `/elements`, { ...request });
}
}
28 changes: 28 additions & 0 deletions client/packages/lowcoder/src/api/orgApi.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,15 @@ import {
UpdateUserOrgRolePayload,
} from "redux/reduxActions/orgActions";
import { ApiResponse, GenericApiResponse } from "./apiResponses";
import {
ApiPaginationResponse,
fetchGroupUserRequestType,
fetchOrgsByEmailRequestType,
fetchOrgUserRequestType,
GenericApiPaginationResponse,
GroupUsersPaginationResponse,
orgGroupRequestType, OrgUsersPaginationResponse
} from "@lowcoder-ee/util/pagination/type";

export interface GroupUsersResponse extends ApiResponse {
data: {
@@ -66,6 +75,10 @@ export class OrgApi extends Api {
return Api.get(OrgApi.fetchGroupURL);
}

static fetchGroupPagination(request: orgGroupRequestType): AxiosPromise<GenericApiPaginationResponse<OrgGroup[]>> {
return Api.get(OrgApi.fetchGroupURL, {...request});
}

static deleteGroup(groupId: string): AxiosPromise<ApiResponse> {
return Api.delete(OrgApi.deleteGroupURL(groupId));
}
@@ -88,10 +101,20 @@ export class OrgApi extends Api {
return Api.get(OrgApi.fetchOrgUsersURL(orgId));
}

static fetchOrgUsersPagination(request:fetchOrgUserRequestType): AxiosPromise<OrgUsersPaginationResponse> {
const {orgId, ...res} = request;
return Api.get(OrgApi.fetchOrgUsersURL(orgId), {...res});
}

static fetchGroupUsers(groupId: string): AxiosPromise<GroupUsersResponse> {
return Api.get(OrgApi.fetchGroupUsersURL(groupId));
}

static fetchGroupUsersPagination(request: fetchGroupUserRequestType): AxiosPromise<GroupUsersPaginationResponse> {
const {groupId, ...res} = request;
return Api.get(OrgApi.fetchGroupUsersURL(groupId), {...res});
}

static deleteGroupUser(request: RemoveGroupUserPayload): AxiosPromise<ApiResponse> {
return Api.delete(OrgApi.deleteGroupUserURL(request.groupId), {
userId: request.userId,
@@ -145,6 +168,11 @@ export class OrgApi extends Api {
static fetchOrgsByEmail(email: string): AxiosPromise<ApiResponse> {
return Api.get(OrgApi.fetchOrgsByEmailURL(email));
}

static fetchOrgsPaginationByEmail(request: fetchOrgsByEmailRequestType): AxiosPromise<ApiPaginationResponse> {
const { email, ...rest } = request;
return Api.get(OrgApi.fetchOrgsByEmailURL(email), {...rest});
}
}

export default OrgApi;
5 changes: 5 additions & 0 deletions client/packages/lowcoder/src/api/queryLibraryApi.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import Api from "./api";
import { AxiosPromise } from "axios";
import { GenericApiResponse } from "./apiResponses";
import { DatasourceType } from "@lowcoder-ee/constants/queryConstants";
import {fetchQueryLibraryPaginationRequestType, GenericApiPaginationResponse} from "@lowcoder-ee/util/pagination/type";

export interface LibraryQuery {
id: string;
@@ -49,6 +50,10 @@ export class QueryLibraryApi extends Api {
return Api.get(QueryLibraryApi.url + `/listByOrg`);
}

static fetchQueryLibraryPaginationByOrg(request: fetchQueryLibraryPaginationRequestType): AxiosPromise<GenericApiPaginationResponse<Array<LibraryQuery>>> {
return Api.get(QueryLibraryApi.url + `/listByOrg`, {...request});
}

static fetchQueryLibraryDropdown(): AxiosPromise<
GenericApiResponse<Array<LibraryQueryDropdownInfo>>
> {
6 changes: 3 additions & 3 deletions client/packages/lowcoder/src/components/TypographyText.tsx
Original file line number Diff line number Diff line change
@@ -40,9 +40,9 @@ const StyledTypographyText = styled(AntdTypographyText)`
`;

export const TypographyText = (props: {
value: string;
editing: boolean;
onChange: (value: string) => void;
value?: string;
editing?: boolean;
onChange?: (value: string) => void;
}) => (
<StyledTypographyText
title={props.value}
Original file line number Diff line number Diff line change
@@ -47,9 +47,9 @@ const children = {
const QueryLibraryCompBase = simpleMultiComp(children);

export const QueryLibraryComp = class extends QueryLibraryCompBase {
propertyView(params: { onPublish: () => void; onHistoryShow: () => void }) {
propertyView(params: { onPublish: () => void; onHistoryShow: () => void; setModify: any; modify: boolean }) {
return (
<PropertyView comp={this} onPublish={params.onPublish} onHistoryShow={params.onHistoryShow} />
<PropertyView comp={this} onPublish={params.onPublish} onHistoryShow={params.onHistoryShow} setModify={params.setModify} modify={params.modify} />
);
}

@@ -99,11 +99,13 @@ function getMetaData(
}

const PropertyView = (props: {
comp: QueryLibraryCompType;
onPublish: () => void;
onHistoryShow: () => void;
comp: QueryLibraryCompType,
onPublish: () => void,
onHistoryShow: () => void,
setModify?: any
modify?: boolean
}) => {
const { comp, onPublish, onHistoryShow } = props;
const { comp, onPublish, onHistoryShow, setModify, modify } = props;

const reduxDispatch = useDispatch();

@@ -157,12 +159,16 @@ const PropertyView = (props: {
CustomModal.confirm({
title: trans("queryLibrary.deleteQueryLabel"),
content: trans("queryLibrary.deleteQueryContent"),
onConfirm: () =>
onConfirm: () =>{
reduxDispatch(
deleteQueryLibrary({
queryLibraryId: comp.children.query.children.id.getView(),
})
),
)
setTimeout(() => {
setModify(!modify);
}, 500);
},
confirmBtnType: "delete",
okText: trans("delete"),
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const BackButton = () =>{
return
<div>123</div>
}
Original file line number Diff line number Diff line change
@@ -185,14 +185,14 @@ function NavLayoutPickModal(props: {
);
}

export const CreateDropdown = (props: { defaultVisible?: boolean; mode: HomeLayoutMode }) => {
const { defaultVisible, mode } = props;
export const CreateDropdown = (props: { defaultVisible?: boolean; mode: HomeLayoutMode; setModify: any; modify: boolean }) => {
const { defaultVisible, mode, setModify, modify} = props;
const [createDropdownVisible, setCreateDropdownVisible] = useState(false);
const [layoutPickerVisible, setLayoutPickerVisible] = useState(false);

const user = useSelector(getUser);

const [handleCreate, isCreating] = useCreateHomeRes();
const [handleCreate, isCreating] = useCreateHomeRes(setModify, modify);

const getCreateMenuItem = (type: HomeResTypeEnum, mode?: HomeLayoutMode): ItemType => {
if (
78 changes: 68 additions & 10 deletions client/packages/lowcoder/src/pages/ApplicationV2/FolderView.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useDispatch, useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import { HomeBreadcrumbType, HomeLayout } from "./HomeLayout";
import { useEffect } from "react";
import { fetchFolderElements } from "../../redux/reduxActions/folderActions";
import { FolderMeta } from "../../constants/applicationConstants";
import {useEffect, useState} from "react";
import {ApplicationMeta, FolderMeta} from "../../constants/applicationConstants";
import { buildFolderUrl } from "../../constants/routesURL";
import { folderElementsSelector, foldersSelector } from "../../redux/selectors/folderSelector";
import { Helmet } from "react-helmet";
import { trans } from "i18n";
import {ApplicationPaginationType} from "@lowcoder-ee/util/pagination/type";
import {fetchFolderElements} from "@lowcoder-ee/util/pagination/axios";

function getBreadcrumbs(
folder: FolderMeta,
@@ -30,12 +31,25 @@ function getBreadcrumbs(
return breadcrumb;
}

interface ElementsState {
elements: ApplicationMeta[];
total: number;
}

export function FolderView() {
const { folderId } = useParams<{ folderId: string }>();

const [elements, setElements] = useState<ElementsState>({ elements: [], total: 0 });
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [searchValues, setSearchValues] = useState("");
const [typeFilter, setTypeFilter] = useState<number>(0);
const [modify, setModify] = useState(true);
const [searchValue, setSearchValue] = useState("");

const dispatch = useDispatch();

const elements = useSelector(folderElementsSelector);
const element = useSelector(folderElementsSelector);
const allFolders = useSelector(foldersSelector);

const folder = allFolders.filter((f) => f.folderId === folderId)[0] || {};
@@ -46,16 +60,60 @@ export function FolderView() {
},
]);

useEffect(() => {
setTimeout(() => {
dispatch(fetchFolderElements({ folderId: folderId }));
}, 100);
}, [folderId]);
useEffect( () => {
try{
fetchFolderElements({
id: folderId,
pageNum:currentPage,
pageSize:pageSize,
applicationType: ApplicationPaginationType[typeFilter],
name: searchValues,
}).then(
(data: any) => {
if (data.success) {
setElements({elements: data.data || [], total: data.total || 1})
}
else
console.error("ERROR: fetchFolderElements", data.error)
}
);
} catch (error) {
console.error('Failed to fetch data:', error);
}
}, [currentPage, pageSize, searchValues, typeFilter, modify]);

useEffect( () => {
if (searchValues !== "")
setCurrentPage(1);
}, [searchValues]
);

useEffect(()=> {
const timer = setTimeout(() => {
if (searchValue.length > 2 || searchValue === "")
setSearchValues(searchValue)
}, 500);
return () => clearTimeout(timer);
}, [searchValue])

return (
<>
<Helmet>{<title>{trans("home.yourFolders")}</title>}</Helmet>
<HomeLayout elements={elements[folderId]} mode={"folder"} breadcrumb={breadcrumbs} />
<HomeLayout
elements={elements.elements}
mode={"folder"}
breadcrumb={breadcrumbs}
currentPage ={currentPage}
setCurrentPage={setCurrentPage}
pageSize={pageSize}
setPageSize={setPageSize}
total={elements.total}
setSearchValue={setSearchValue}
searchValue={searchValue}
setTypeFilterPagination={setTypeFilter}
setModify={setModify}
modify={modify}
/>
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import styled from "styled-components";
import { HomeRes } from "./HomeLayout";
import { HomeResCard } from "./HomeResCard";
import { MarketplaceResCard } from "./MarketplaceResCard";
import {Back, HomeResCard} from "./HomeResCard";
import { MarketplaceResCard} from "./MarketplaceResCard";
import React, { useState } from "react";
import { MoveToFolderModal } from "./MoveToFolderModal";

@@ -19,17 +19,19 @@ const ApplicationCardsWrapper = styled.div`
}
`;

export function HomeCardView(props: { resources: HomeRes[] }) {
export function HomeCardView(props: { resources: HomeRes[], setModify?: any, modify?: boolean, mode?: string }) {
const {setModify, modify,mode} = props;
const [needMoveRes, setNeedMoveRes] = useState<HomeRes | undefined>(undefined);

return (
<ApplicationCardsWrapper>
<Back mode={mode!}/>
{props.resources.map((res) => (
res.isMarketplace ?
<MarketplaceResCard key={res.id} res={res} /> :
<HomeResCard key={res.id} res={res} onMove={setNeedMoveRes} />
<HomeResCard key={res.id} res={res} onMove={setNeedMoveRes} setModify={setModify} modify={modify!} />
))}
<MoveToFolderModal source={needMoveRes} onClose={() => setNeedMoveRes(undefined)} />
<MoveToFolderModal source={needMoveRes} onClose={() => setNeedMoveRes(undefined)} setModify={setModify} modify={modify!} />
</ApplicationCardsWrapper>
);
}
139 changes: 110 additions & 29 deletions client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@ import { isFetchingFolderElements } from "../../redux/selectors/folderSelector";
import { checkIsMobile } from "util/commonUtils";
import { default as Divider } from "antd/es/divider";
import { ApplicationCategoriesEnum } from "constants/applicationConstants";
import { Pagination } from 'antd';

const Wrapper = styled.div`
display: flex;
@@ -199,6 +200,12 @@ const EmptyView = styled.div`
}
}
`;
const PaginationLayout = styled.div`
display: flex;
justify-content: center;
margin-top: 20px;
margin-bottom: 20px;
`

const LayoutSwitcher = styled.div`
position: absolute;
@@ -301,11 +308,43 @@ export interface HomeLayoutProps {
localMarketplaceApps?: Array<ApplicationMeta>;
globalMarketplaceApps?: Array<ApplicationMeta>;
mode: HomeLayoutMode;
setCurrentPage?: any;
setPageSize?: any;
currentPage?: number;
pageSize?: number;
total?: number;
searchValue?: string;
setSearchValue?: any;
setTypeFilterPagination?: any;
setModify?: any;
modify?: boolean;
}

export function HomeLayout(props: HomeLayoutProps) {
const { breadcrumb = [],
elements = [],
localMarketplaceApps = [],
globalMarketplaceApps = [],
mode ,
setCurrentPage,
setPageSize,
pageSize,
currentPage,
searchValue,
setSearchValue,
total,
setTypeFilterPagination,
setModify,
modify

} = props;
const handlePageChange = (page: number) => {
setCurrentPage(page);
};

const { breadcrumb = [], elements = [], localMarketplaceApps = [], globalMarketplaceApps = [], mode } = props;
const handlePageSizeChange = (current: number, size: number) => {
setPageSize(size);
};

const categoryOptions = [
{ label: <FilterMenuItem>{trans("home.allCategories")}</FilterMenuItem>, value: 'All' },
@@ -324,7 +363,7 @@ export function HomeLayout(props: HomeLayoutProps) {
const isSelfHost = window.location.host !== 'app.lowcoder.cloud';
const [typeFilter, setTypeFilter] = useState<HomeResKey>("All");
const [categoryFilter, setCategoryFilter] = useState<ApplicationCategoriesEnum | "All">("All");
const [searchValue, setSearchValue] = useState("");
const [visibility, setVisibility] = useState(mode === "view" || mode === "trash" || mode === "folder");
const [layout, setLayout] = useState<HomeLayoutType>(
checkIsMobile(window.innerWidth) ? "card" : getHomeLayout()
);
@@ -342,7 +381,15 @@ export function HomeLayout(props: HomeLayoutProps) {
return null;
}

var displayElements = elements;
var displayElements = elements.sort((a, b) => {
if (a.folder && !b.folder) {
return -1;
} else if (!a.folder && b.folder) {
return 1;
} else {
return 0;
}
});

if (mode === "marketplace" && isSelfHost) {
const markedLocalApps = localMarketplaceApps.map(app => ({ ...app, isLocalMarketplace: true }));
@@ -354,27 +401,34 @@ export function HomeLayout(props: HomeLayoutProps) {
const markedLocalApps = localMarketplaceApps.map(app => ({ ...app, isLocalMarketplace: true }));
displayElements = [...markedLocalApps];
}

const resList: HomeRes[] = displayElements
.filter((e) =>
searchValue
? e.name?.toLocaleLowerCase().includes(searchValue?.toLocaleLowerCase()) ||
e.createBy?.toLocaleLowerCase().includes(searchValue?.toLocaleLowerCase())
: true
)
.filter((e) => {
if (HomeResTypeEnum[typeFilter].valueOf() === HomeResTypeEnum.All) {
if (!visibility) {
if (searchValue) {
const lowerCaseSearchValue = searchValue.toLocaleLowerCase();
return e.name?.toLocaleLowerCase().includes(lowerCaseSearchValue) ||
e.createBy?.toLocaleLowerCase().includes(lowerCaseSearchValue);
}
return true;
}
if (e.folder) {
return HomeResTypeEnum[typeFilter] === HomeResTypeEnum.Folder;
} else {
if (typeFilter === "Navigation") {
return NavigationTypes.map((t) => t.valueOf()).includes(e.applicationType);
return true;
})
.filter((e) => {
if(!visibility) {
if (HomeResTypeEnum[typeFilter].valueOf() === HomeResTypeEnum.All) {
return true;
}
if (e.folder) {
return HomeResTypeEnum[typeFilter] === HomeResTypeEnum.Folder;
} else {
if (typeFilter === "Navigation") {
return NavigationTypes.map((t) => t.valueOf()).includes(e.applicationType);
}
return HomeResTypeEnum[typeFilter].valueOf() === e.applicationType;
}
return HomeResTypeEnum[typeFilter].valueOf() === e.applicationType;
}
})
return true;
})
.filter((e) => {
// If "All" is selected, do not filter out any elements based on category
if (categoryFilter === 'All' || !categoryFilter) {
@@ -415,6 +469,7 @@ export function HomeLayout(props: HomeLayoutProps) {
}
);


const getFilterMenuItem = (type: HomeResTypeEnum) => {
const Icon = HomeResInfo[type].icon;
return {
@@ -462,7 +517,7 @@ export function HomeLayout(props: HomeLayoutProps) {

{showNewUserGuide(user) && <HomepageTourV2 />}

<HomeView>
<HomeView>
<StyleHomeCover>
<h1 style={{color: "#ffffff", marginTop : "12px"}}>
{mode === "marketplace" && trans("home.appMarketplace")}
@@ -480,17 +535,32 @@ export function HomeLayout(props: HomeLayoutProps) {
<FilterDropdown
variant="borderless"
value={typeFilter}
onChange={(value: any) => setTypeFilter(value as HomeResKey)}
onChange={(value: any) => {
setTypeFilter(value as HomeResKey);
if(visibility)
setTypeFilterPagination(HomeResTypeEnum[value])
}
}
options={[
getFilterMenuItem(HomeResTypeEnum.All),
getFilterMenuItem(HomeResTypeEnum.Application),
getFilterMenuItem(HomeResTypeEnum.Module),
...(mode !== "marketplace" ? [getFilterMenuItem(HomeResTypeEnum.Navigation)] : []),
...(mode !== "marketplace" ? [getFilterMenuItem(HomeResTypeEnum.Navigation), getFilterMenuItem(HomeResTypeEnum.MobileTabLayout)] : []),
...(mode !== "trash" && mode !== "marketplace" ? [getFilterMenuItem(HomeResTypeEnum.Folder)] : []),
]}
getPopupContainer={(node: any) => node}
suffixIcon={<ArrowSolidIcon />} />
)}
{mode === "view" &&
<FilterDropdown
style={{ minWidth: "220px" }}
variant="borderless"
value={categoryFilter}
onChange={(value: any) => setCategoryFilter(value as ApplicationCategoriesEnum)}
options={categoryOptions}
// getPopupContainer={(node) => node}
suffixIcon={<ArrowSolidIcon />}
/>}
{mode === "marketplace" && (
<FilterDropdown
style={{ minWidth: "220px" }}
@@ -505,12 +575,12 @@ export function HomeLayout(props: HomeLayoutProps) {
<OperationRightWrapper>
<Search
placeholder={trans("search")}
value={searchValue}
value={searchValue || ""}
onChange={(e) => setSearchValue(e.target.value)}
style={{ width: "192px", height: "32px", margin: "0" }}
/>
{mode !== "trash" && mode !== "marketplace" && user.orgDev && (
<CreateDropdown defaultVisible={showNewUserGuide(user)} mode={mode} />
<CreateDropdown defaultVisible={showNewUserGuide(user)} mode={mode} setModify={setModify} modify={modify!} />
)}
</OperationRightWrapper>
</OperationWrapper>
@@ -526,7 +596,7 @@ export function HomeLayout(props: HomeLayoutProps) {
{resList.length > 0 ? (
<>
{mode === "trash" ? (
<TrashTableView resources={resList} />
<TrashTableView resources={resList} setModify={setModify} modify={modify!}/>
) : (
<>
<LayoutSwitcher onClick={() => setLayout(layout === "list" ? "card" : "list")}>
@@ -575,9 +645,9 @@ export function HomeLayout(props: HomeLayoutProps) {
{mode !== "marketplace" && (
<>
{layout === "list" ? (
<HomeTableView resources={resList} />
<HomeTableView resources={resList} setModify={setModify} modify={modify!} mode={mode}/>
) : (
<HomeCardView resources={resList} />
<HomeCardView resources={resList} setModify={setModify} modify={modify!} mode={mode} />
)}
</>
)}
@@ -597,16 +667,27 @@ export function HomeLayout(props: HomeLayoutProps) {
? trans("home.projectEmptyCanAdd")
: trans("home.projectEmpty")}
</div>
{mode !== "trash" && mode !== "marketplace" && user.orgDev && <CreateDropdown mode={mode} />}
{mode !== "trash" && mode !== "marketplace" && user.orgDev && <CreateDropdown mode={mode} setModify={setModify} modify={modify!}/>}
</EmptyView>
)}
</>
)}
</ContentWrapper>

{visibility && resList.length ? <div>
<PaginationLayout>
<Pagination
current={currentPage}
pageSize={pageSize}
onChange={handlePageChange}
onShowSizeChange={handlePageSizeChange}
total={total}
showSizeChanger
/>
</PaginationLayout>
</div> : null}
</Card>

</HomeView>
</HomeView>

</Wrapper>
);
40 changes: 38 additions & 2 deletions client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import { HomeRes } from "./HomeLayout";
import { HomeResTypeEnum } from "../../types/homeRes";
import { updateFolder } from "../../redux/reduxActions/folderActions";
import {
backFolderViewClick,
handleAppEditClick,
handleAppViewClick,
handleFolderViewClick,
@@ -23,6 +24,7 @@ import { TypographyText } from "../../components/TypographyText";
import { useParams } from "react-router-dom";
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
import { colorPickerEvent } from "@lowcoder-ee/comps/comps/mediaComp/colorPickerComp";
import {FolderIcon} from "icons";

const EditButton = styled(TacoButton)`
width: 52px;
@@ -141,8 +143,8 @@ const OperationWrapper = styled.div`

const MONTH_MILLIS = 30 * 24 * 60 * 60 * 1000;

export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => void }) {
const { res, onMove } = props;
export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => void; setModify:any; modify: boolean }) {
const { res, onMove, setModify, modify } = props;
const [appNameEditing, setAppNameEditing] = useState(false);
const dispatch = useDispatch();

@@ -214,10 +216,16 @@ export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => voi
}
if (res.type === HomeResTypeEnum.Folder) {
dispatch(updateFolder({ id: res.id, name: value }));
setTimeout(() => {
setModify(!modify);
}, 200);
} else {
dispatch(
updateAppMetaAction({ applicationId: res.id, name: value, folderId: folderId })
);
setTimeout(() => {
setModify(!modify);
}, 200);
}
setAppNameEditing(false);
}}
@@ -245,9 +253,37 @@ export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => voi
res={res}
onRename={() => setAppNameEditing(true)}
onMove={(res) => onMove(res)}
setModify={setModify}
modify={modify}
/>
</OperationWrapper>
</Card>
</Wrapper>
);
}

export function Back(props: { mode: string }) {
const { mode } = props;
return mode === "folder" ?
<Wrapper style={{cursor: "pointer"}}>
<Card>
<FolderIcon width={"42px"} height={"42px"} style={
{
marginRight: "10px",
flexShrink: 0
}
} />
<CardInfo
onClick={(e) => {
backFolderViewClick();
}}
>
<TypographyText
/>
<h1 style={{fontSize:"x-large"}}>...</h1>
<AppTimeOwnerInfoLabel title={""}></AppTimeOwnerInfoLabel>
</CardInfo>
</Card>
</Wrapper>
: <></>;
}
53 changes: 32 additions & 21 deletions client/packages/lowcoder/src/pages/ApplicationV2/HomeResOptions.tsx
Original file line number Diff line number Diff line change
@@ -38,8 +38,10 @@ export const HomeResOptions = (props: {
onDuplicate?: (res: HomeRes | undefined) => void;
onRename: (res: HomeRes) => void;
onMove: (res: HomeRes) => void;
setModify: any;
modify: boolean;
}) => {
const { res, onDuplicate, onRename, onMove } = props;
const { res, onDuplicate, onRename, onMove, setModify, modify } = props;
const dispatch = useDispatch();
const [showCopyModal, setShowCopyModal] = useState(false);

@@ -78,19 +80,24 @@ export const HomeResOptions = (props: {
type: HomeResInfo[res.type].name,
name: <b>{res.name}</b>,
}),
onConfirm: () =>
onConfirm: () =>{
new Promise((resolve, reject) => {
dispatch(
recycleApplication(
{ applicationId: res.id, folderId: folderId },
() => {
messageInstance.success(trans("success"));
resolve(true);
},
() => reject()
)
recycleApplication(
{ applicationId: res.id, folderId: folderId },
() => {
messageInstance.success(trans("success"));
resolve(true);
},
() => reject()
)
);
}),
setTimeout(() => {
setModify(!modify);
}, 200);
})

},
confirmBtnType: "delete",
okText: trans("home.moveToTrash"),
});
@@ -115,19 +122,23 @@ export const HomeResOptions = (props: {
type: HomeResInfo[res.type].name.toLowerCase(),
name: <b>{res.name}</b>,
}),
onConfirm: () =>
onConfirm: () =>{
new Promise((resolve, reject) => {
dispatch(
dispatch(
deleteFolder(
{ folderId: res.id, parentFolderId: folderId },
() => {
messageInstance.success(trans("home.deleteSuccessMsg"));
resolve(true);
},
() => reject()
{ folderId: res.id, parentFolderId: folderId },
() => {
messageInstance.success(trans("home.deleteSuccessMsg"));
resolve(true);
},
() => reject()
)
);
}),
);
})
setTimeout(() => {
setModify(!modify);
}, 200);
},
confirmBtnType: "delete",
okText: trans("delete"),
});
61 changes: 44 additions & 17 deletions client/packages/lowcoder/src/pages/ApplicationV2/HomeTableView.tsx
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import { TacoButton } from "lowcoder-design/src/components/button"
import styled from "styled-components";
import { useDispatch } from "react-redux";
import {
backFolderViewClick,
handleAppEditClick,
handleAppViewClick,
handleFolderViewClick,
@@ -51,7 +52,8 @@ const TypographyText = styled(AntdTypographyText)`
width: 100%;
`;

export const HomeTableView = (props: { resources: HomeRes[] }) => {
export const HomeTableView = (props: { resources: HomeRes[], setModify?: any, modify?: boolean, mode?: string }) => {
const {setModify, modify, resources, mode} = props
const dispatch = useDispatch();

const { folderId } = useParams<{ folderId: string }>();
@@ -60,26 +62,43 @@ export const HomeTableView = (props: { resources: HomeRes[] }) => {
const [needDuplicateRes, setNeedDuplicateRes] = useState<HomeRes | undefined>(undefined);
const [needMoveRes, setNeedMoveRes] = useState<HomeRes | undefined>(undefined);

const back: HomeRes = {
key: "",
id: "",
name: ". . .",
type: 4,
creator: "",
lastModifyTime: 0,
isManageable: false,
isDeletable: false
}
if (mode === "folder"){
resources.unshift(back)
}

return (
<>
<Table
style={{ padding: "0 24px 80px", color: "#8B8FA3" }}
style={{ padding: "0 24px 60px", color: "#8B8FA3" }}
tableLayout={"auto"}
scroll={{ x: "100%" }}
pagination={false}
onRow={(record) => ({
onClick: (e) => {
// console.log(e.target);
const item = record as HomeRes;
if (needRenameRes?.id === item.id || needDuplicateRes?.id === item.id) {
return;
}
if (item.type === HomeResTypeEnum.Folder) {
handleFolderViewClick(item.id);
} else if(item.isMarketplace) {
handleMarketplaceAppViewClick(item.id);
} else {
item.isEditable ? handleAppEditClick(e, item.id) : handleAppViewClick(item.id);
if (mode === "folder" && record.type === 4){
backFolderViewClick()
} else{
const item = record as HomeRes;
if (needRenameRes?.id === item.id || needDuplicateRes?.id === item.id) {
return;
}
if (item.type === HomeResTypeEnum.Folder) {
handleFolderViewClick(item.id);
} else if(item.isMarketplace) {
handleMarketplaceAppViewClick(item.id);
} else {
item.isEditable ? handleAppEditClick(e, item.id) : handleAppViewClick(item.id);
}
}
},
})}
@@ -122,6 +141,9 @@ export const HomeTableView = (props: { resources: HomeRes[] }) => {
}
if (item.type === HomeResTypeEnum.Folder) {
dispatch(updateFolder({ id: item.id, name: value }));
setTimeout(() => {
setModify(!modify);
}, 200);
} else {
dispatch(
updateAppMetaAction({
@@ -130,6 +152,9 @@ export const HomeTableView = (props: { resources: HomeRes[] }) => {
folderId: folderId,
})
);
setTimeout(() => {
setModify(!modify);
}, 200);
}
setNeedRenameRes(undefined);
},
@@ -154,7 +179,7 @@ export const HomeTableView = (props: { resources: HomeRes[] }) => {
},
render: (_, record) => (
<SubColumnCell>
{HomeResInfo[(record as any).type as HomeResTypeEnum].name}
{ mode === "folder" && record.type === 4 ? "" : HomeResInfo[(record as any).type as HomeResTypeEnum].name }
</SubColumnCell>
),
},
@@ -216,7 +241,7 @@ export const HomeTableView = (props: { resources: HomeRes[] }) => {
? handleMarketplaceAppViewClick(item.id)
: handleAppViewClick(item.id);
}}
style={{ marginRight: "52px" }}
style={{ marginRight: "52px", display: mode === "folder" && record.type === 4 ? "none" : "block" }}
>
{trans("view")}
</EditBtn>
@@ -225,15 +250,17 @@ export const HomeTableView = (props: { resources: HomeRes[] }) => {
onDuplicate={(res) => setNeedDuplicateRes(res)}
onRename={(res) => setNeedRenameRes(res)}
onMove={(res) => setNeedMoveRes(res)}
setModify={setModify}
modify={modify!}
/>
</OperationWrapper>
);
},
},
]}
dataSource={props.resources}
dataSource={resources}
/>
<MoveToFolderModal source={needMoveRes} onClose={() => setNeedMoveRes(undefined)} />
<MoveToFolderModal source={needMoveRes} onClose={() => setNeedMoveRes(undefined)} setModify={setModify} modify={modify!} />
</>
);
};
70 changes: 65 additions & 5 deletions client/packages/lowcoder/src/pages/ApplicationV2/HomeView.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,62 @@
import { useSelector } from "react-redux";
import { HomeLayout } from "./HomeLayout";
import { getUser } from "../../redux/selectors/usersSelectors";
import { folderElementsSelector } from "../../redux/selectors/folderSelector";
import { Helmet } from "react-helmet";
import { trans } from "i18n";
import {useState, useEffect } from "react";
import {fetchFolderElements} from "@lowcoder-ee/util/pagination/axios";
import {ApplicationMeta, FolderMeta} from "@lowcoder-ee/constants/applicationConstants";
import {ApplicationPaginationType} from "@lowcoder-ee/util/pagination/type";

interface ElementsState {
elements: (ApplicationMeta | FolderMeta)[];
total: number;
}

export function HomeView() {
const elements = useSelector(folderElementsSelector)[""];
const [elements, setElements] = useState<ElementsState>({ elements: [], total: 1 });
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [searchValue, setSearchValue] = useState("");
const [searchValues, setSearchValues] = useState("");
const [typeFilter, setTypeFilter] = useState<number>(0);
const [modify, setModify] = useState(true);
useEffect( () => {
try{
fetchFolderElements({
pageNum:currentPage,
pageSize:pageSize,
applicationType: ApplicationPaginationType[typeFilter],
name: searchValues,
}).then(
(data: any) => {
if (data.success) {
setElements({elements: data.data || [], total: data.total || 1})
}
else
console.error("ERROR: fetchFolderElements", data.error)
}
);
} catch (error) {
console.error('Failed to fetch data:', error);
}
}, [currentPage, pageSize, searchValues, typeFilter, modify]
);

useEffect( () => {
if (searchValues !== "")
setCurrentPage(1);
}, [searchValues]
);

useEffect(()=> {
const timer = setTimeout(() => {
if (searchValue.length > 2 || searchValue === "")
setSearchValues(searchValue)
}, 500);
return () => clearTimeout(timer);
}, [searchValue])

const user = useSelector(getUser);

if (!user.currentOrgId) {
@@ -16,9 +66,19 @@ export function HomeView() {
return (
<>
<Helmet>{<title>{trans("productName")} {trans("home.home")}</title>}</Helmet>
<HomeLayout
elements={elements}
mode={"view"}
<HomeLayout
elements={elements.elements}
mode={"view"}
currentPage ={currentPage}
setCurrentPage={setCurrentPage}
pageSize={pageSize}
setPageSize={setPageSize}
total={elements.total}
setSearchValue={setSearchValue}
searchValue={searchValue}
setTypeFilterPagination={setTypeFilter}
setModify={setModify}
modify={modify}
/>
</>
);
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ import { Helmet } from "react-helmet";
export function MarketplaceView() {
const [ marketplaceApps, setMarketplaceApps ] = useState<Array<ApplicationMeta>>([]);
const [ localMarketplaceApps, setLocalMarketplaceApps ] = useState<Array<ApplicationMeta>>([]);
const [searchValue, setSearchValue] = useState("");

const fetchMarketplaceApps = async () => {
try {
@@ -60,7 +61,10 @@ export function MarketplaceView() {
localMarketplaceApps={localMarketplaceApps}
globalMarketplaceApps={marketplaceApps}
breadcrumb={[{ text: trans("home.marketplace"), path: MARKETPLACE_URL }]}
mode={"marketplace"} />
mode={"marketplace"}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -40,7 +40,8 @@ const MoveModalFooter = styled.div`
gap: 8px;
`;

export const MoveToFolderModal = (props: { source?: HomeRes; onClose: () => void }) => {
export const MoveToFolderModal = (props: { source?: HomeRes; onClose: () => void, setModify: any, modify: boolean }) => {
const {setModify, modify} = props;
const [form] = Form.useForm();

const [loading, setLoading] = useState<boolean>(false);
@@ -83,6 +84,9 @@ export const MoveToFolderModal = (props: { source?: HomeRes; onClose: () => void
() => setLoading(false)
)
);
setTimeout(() => {
setModify(!modify);
}, 200);
});
}}
>

This file was deleted.

30 changes: 20 additions & 10 deletions client/packages/lowcoder/src/pages/ApplicationV2/TrashTableView.tsx
Original file line number Diff line number Diff line change
@@ -32,7 +32,8 @@ const EditBtn = styled(TacoButton)`
height: 24px;
`;

export const TrashTableView = (props: { resources: HomeRes[] }) => {
export const TrashTableView = (props: { resources: HomeRes[] , setModify: any, modify: boolean }) => {
const {resources, setModify, modify} = props;
const dispatch = useDispatch();

return (
@@ -119,13 +120,17 @@ export const TrashTableView = (props: { resources: HomeRes[] }) => {
style={{ padding: "0 8px", width: "fit-content", minWidth: "52px" }}
buttonType={"blue"}
className={"home-datasource-edit-button"}
onClick={() =>
dispatch(
restoreApplication({ applicationId: item.id }, () => {
messageInstance.success(trans("home.recoverSuccessMsg"));
})
)
onClick={() =>{
dispatch(
restoreApplication({ applicationId: item.id }, () => {
messageInstance.success(trans("home.recoverSuccessMsg"));
})
)
setTimeout(() => {
setModify(!modify);
}, 200);
}
}
>
{trans("recover")}
</EditBtn>
@@ -140,7 +145,7 @@ export const TrashTableView = (props: { resources: HomeRes[] }) => {
type: HomeResInfo[item.type].name.toLowerCase(),
name: <b>{item.name}</b>,
}),
onConfirm: () =>
onConfirm: () =>{
new Promise((resolve, reject) => {
dispatch(
deleteApplication(
@@ -152,10 +157,15 @@ export const TrashTableView = (props: { resources: HomeRes[] }) => {
() => reject()
)
);
}),
})
setTimeout(() => {
setModify(!modify);
}, 200);
},
confirmBtnType: "delete",
okText: trans("delete"),
})

}
style={{ marginLeft: "12px", width: "76px" }}
>
@@ -166,7 +176,7 @@ export const TrashTableView = (props: { resources: HomeRes[] }) => {
},
},
]}
dataSource={props.resources}
dataSource={resources}
/>
);
};
76 changes: 64 additions & 12 deletions client/packages/lowcoder/src/pages/ApplicationV2/TrashView.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,79 @@
import { HomeLayout } from "./HomeLayout";
import { useDispatch, useSelector } from "react-redux";
import { recycleListSelector } from "../../redux/selectors/applicationSelector";
import { TRASH_URL } from "../../constants/routesURL";
import { useEffect } from "react";
import { fetchApplicationRecycleList } from "../../redux/reduxActions/applicationActions";
import {useEffect, useState} from "react";
import { trans } from "../../i18n";
import { Helmet } from "react-helmet";
import {fetchApplicationElements} from "@lowcoder-ee/util/pagination/axios";

interface ElementsState {
elements: any;
total: number;
}

export function TrashView() {
const dispatch = useDispatch();
const recycleList = useSelector(recycleListSelector);
const [elements, setElements] = useState<ElementsState>({ elements: [], total: 1 });
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [searchValues, setSearchValues] = useState("");
const [searchValue, setSearchValue] = useState("");
const [typeFilter, setTypeFilter] = useState<number>(0);
const [modify, setModify] = useState(false);

useEffect(() => {
dispatch(fetchApplicationRecycleList());
}, [dispatch]);
useEffect( () => {
try{
fetchApplicationElements({
pageNum:currentPage,
pageSize:pageSize,
applicationType: typeFilter === 7 ? 3 : typeFilter, // // Application of Navigation is 3 in API.
name: searchValues,
}).then(
data => {
if (data.success) {
setElements({elements: data.data || [], total: data.total || 1})
}
else
console.error("ERROR: fetchFolderElements", data.error)
}
);
} catch (error) {
console.error('Failed to fetch data:', error);
}
}, [currentPage, pageSize, searchValues, typeFilter, modify]
);
useEffect( () => {
if (searchValues !== "")
setCurrentPage(1);
}, [searchValues]
);

//debouncing
useEffect(()=> {
const timer = setTimeout(() => {
if (searchValue.length > 2 || searchValue === "")
setSearchValues(searchValue)
}, 500);
return () => clearTimeout(timer);
}, [searchValue])

return (
<>
<Helmet>{<title>{trans("home.trash")}</title>}</Helmet>
<HomeLayout
elements={recycleList}
breadcrumb={[{ text: trans("home.trash"), path: TRASH_URL }]}
mode={"trash"} />
elements={elements.elements}
breadcrumb={[{ text: trans("home.trash"), path: TRASH_URL }]}
mode={"trash"}
currentPage ={currentPage}
setCurrentPage={setCurrentPage}
pageSize={pageSize}
setPageSize={setPageSize}
total={elements.total}
setSearchValue={setSearchValue}
searchValue={searchValue}
setTypeFilterPagination={setTypeFilter}
setModify={setModify}
modify={modify}
/>
</>
);
}

14 changes: 6 additions & 8 deletions client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ import {
DATASOURCE_URL,
FOLDER_URL,
FOLDER_URL_PREFIX,
FOLDERS_URL,
MARKETPLACE_URL,
QUERY_LIBRARY_URL,
SETTING_URL,
@@ -53,7 +52,6 @@ import { FolderView } from "./FolderView";
import { TrashView } from "./TrashView";
import { MarketplaceView } from "./MarketplaceView";
// import { SideBarItemType } from "../../components/layout/SideBarSection";
import { RootFolderListView } from "./RootFolderListView";
// import InviteDialog from "../common/inviteDialog";
import { fetchFolderElements, updateFolder } from "../../redux/reduxActions/folderActions";
// import { ModuleView } from "./ModuleView";
@@ -262,12 +260,12 @@ export default function ApplicationHome() {

{
items: [
{
text: <MoreFoldersWrapper>{trans("home.allFolders")}</MoreFoldersWrapper>,
routePath: FOLDERS_URL,
routeComp: RootFolderListView,
icon: ({ selected, ...otherProps }) => selected ? <FolderIcon {...otherProps} width={"24px"}/> : <FolderIcon {...otherProps} width={"24px"}/>,
},
// {
// text: <MoreFoldersWrapper>{trans("home.allFolders")}</MoreFoldersWrapper>,
// routePath: FOLDERS_URL,
// routeComp: RootFolderListView,
// icon: ({ selected, ...otherProps }) => selected ? <FolderIcon {...otherProps} width={"24px"}/> : <FolderIcon {...otherProps} width={"24px"}/>,
// },
{
text: <TabLabel>{trans("home.allApplications")}</TabLabel>,
routePath: ALL_APPLICATIONS_URL,
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ const CreateFolderLabel = styled.div`
margin-bottom: 8px;
`;

export function useCreateFolder() {
export function useCreateFolder(setModify: any, modify: boolean) {
const dispatch = useDispatch();
const user = useSelector(getUser);
const allFolders = useSelector(foldersSelector);
@@ -73,7 +73,7 @@ export function useCreateFolder() {
</FormSection>
</DatasourceForm>
),
onConfirm: () =>
onConfirm: () =>{
form.validateFields().then(
() =>
new Promise((resolve, reject) => {
@@ -82,7 +82,11 @@ export function useCreateFolder() {
() => reject(false)
);
})
),
)
setTimeout(() => {
setModify(!modify);
}, 200);
},
okText: trans("create"),
});
}, [user, allFolders, form, dispatch]);
Original file line number Diff line number Diff line change
@@ -31,15 +31,15 @@ export const newAppPrefix = (userName: string, appType: AppTypeEnum = AppTypeEnu
return trans("home.newApp", { userName: userName, name: toLower(HomeResInfo[appType].name) });
};

export function useCreateHomeRes() {
export function useCreateHomeRes(setModify:any, modify: boolean) {
const dispatch = useDispatch();
const user = useSelector(getUser);
const allApplications = useSelector(normalAppListSelector);
const isCreating = useSelector(isApplicationCreating);

const { folderId } = useParams<{ folderId: string }>();

const handleFolderCreate = useCreateFolder();
const handleFolderCreate = useCreateFolder(setModify, modify);

const handleCreate = useCallback(
(type: HomeResTypeEnum) => {
80 changes: 64 additions & 16 deletions client/packages/lowcoder/src/pages/datasource/datasourceList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled from "styled-components";
import { EditPopover, PointIcon, Search, TacoButton } from "lowcoder-design";
import React, { useState } from "react";
import React, {useEffect, useState} from "react";
import { useDispatch, useSelector } from "react-redux";
import { getDataSource, getDataSourceLoading, getDataSourceTypesMap } from "../../redux/selectors/datasourceSelectors";
import { deleteDatasource } from "../../redux/reduxActions/datasourceActions";
@@ -17,6 +17,10 @@ import { DatasourcePermissionDialog } from "../../components/PermissionDialog/Da
import DataSourceIcon from "components/DataSourceIcon";
import { Helmet } from "react-helmet";
import LoadingOutlined from "@ant-design/icons/LoadingOutlined";
import PaginationComp from "@lowcoder-ee/util/pagination/Pagination";
import {DatasourceInfo} from "@lowcoder-ee/api/datasourceApi";
import {fetchDatasourcePagination} from "@lowcoder-ee/util/pagination/axios";
import {getUser} from "@lowcoder-ee/redux/selectors/usersSelectors";

const DatasourceWrapper = styled.div`
display: flex;
@@ -103,11 +107,54 @@ const StyledTable = styled(Table)`
export const DatasourceList = () => {
const dispatch = useDispatch();
const [searchValue, setSearchValue] = useState("");
const [searchValues, setSearchValues] = useState("");
const [isCreateFormShow, showCreateForm] = useState(false);
const [shareDatasourceId, setShareDatasourceId] = useState<string | undefined>(undefined);
const datasource = useSelector(getDataSource);
const [modify, setModify] = useState(false);
const currentUser = useSelector(getUser);
const orgId = currentUser.currentOrgId;
const datasourceLoading = useSelector(getDataSourceLoading);
const plugins = useSelector(getDataSourceTypesMap);
interface ElementsState {
elements: DatasourceInfo[];
total: number;
}

const [elements, setElements] = useState<ElementsState>({ elements: [], total: 0 });
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);

useEffect(()=> {
const timer = setTimeout(() => {
if (searchValue.length > 2 || searchValue === "")
setSearchValues(searchValue)
}, 500);
return () => clearTimeout(timer);
}, [searchValue])

useEffect( () => {
fetchDatasourcePagination(
{
orgId: orgId,
pageNum: currentPage,
pageSize: pageSize,
name: searchValues
}
).then((result: any) => {
if (result.success){
setElements({elements: result.data || [], total: result.total || 1})
}
else
console.error("ERROR: fetchFolderElements", result.error)
})
}, [currentPage, pageSize, searchValues, modify]
)

useEffect( () => {
if (searchValues !== "")
setCurrentPage(1);
}, [searchValues]
);

return (
<>
@@ -254,6 +301,10 @@ export const DatasourceList = () => {
text: trans("delete"),
onClick: () => {
dispatch(deleteDatasource({ datasourceId: record.id }));
setTimeout(() => {
setModify(!modify);
}, 500);

},
type: "delete",
},
@@ -267,19 +318,7 @@ export const DatasourceList = () => {
),
},
]}
dataSource={datasource
.filter((info) => {
if (info.datasource.creationSource === 2) {
return false;
}
if (!isEmpty(searchValue)) {
return (
info.datasource.name.toLowerCase().includes(searchValue.trim().toLowerCase()) ||
info.datasource.type.toLowerCase().includes(searchValue.trim().toLowerCase())
);
}
return true;
})
dataSource={elements.elements
.map((info, i) => ({
key: i,
id: info.datasource.id,
@@ -296,6 +335,13 @@ export const DatasourceList = () => {
creator: info.creatorName,
edit: info.edit,
}))} />
{ !!elements.elements.length ? <PaginationComp
currentPage={currentPage}
pageSize={pageSize}
setPageSize={setPageSize}
setCurrentPage={setCurrentPage}
total={elements.total}
/> : <></>}
</BodyWrapper>
{shareDatasourceId && (
<DatasourcePermissionDialog
@@ -305,6 +351,8 @@ export const DatasourceList = () => {
!visible && setShareDatasourceId(undefined);
} } />
)}
</DatasourceWrapper></>
</DatasourceWrapper>

</>
);
};
23 changes: 21 additions & 2 deletions client/packages/lowcoder/src/pages/editor/AppEditor.tsx
Original file line number Diff line number Diff line change
@@ -37,6 +37,8 @@ import { currentApplication } from "@lowcoder-ee/redux/selectors/applicationSele
import { notificationInstance } from "components/GlobalInstances";
import { AppState } from "@lowcoder-ee/redux/reducers";
import { resetIconDictionary } from "@lowcoder-ee/constants/iconConstants";
import {fetchJsDSPaginationByApp} from "@lowcoder-ee/util/pagination/axios";
import PaginationComp from "@lowcoder-ee/util/pagination/Pagination";

const AppSnapshot = lazy(() => {
return import("pages/editor/appSnapshot")
@@ -57,6 +59,9 @@ const AppEditor = React.memo(() => {
const fetchOrgGroupsFinished = useSelector(getFetchOrgGroupsFinished);
const isCommonSettingsFetching = useSelector(getIsCommonSettingFetching);
const application = useSelector(currentApplication);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [elements, setElements] = useState({ elements: [], total: 1 })
const isLowcoderCompLoading = useSelector((state: AppState) => state.npmPlugin.loading.lowcoderComps);

const isUserViewMode = useMemo(
@@ -140,8 +145,13 @@ const AppEditor = React.memo(() => {
}, [dispatch, applicationId, paramViewMode]);

const fetchJSDataSourceByApp = useCallback(() => {
DatasourceApi.fetchJsDatasourceByApp(applicationId).then((res) => {
res.data.data.forEach((i) => {
fetchJsDSPaginationByApp({
appId: applicationId,
pageNum: currentPage,
pageSize: pageSize
}).then((res) => {
setElements({elements: [], total: res.total || 1})
res.data!.forEach((i: any) => {
registryDataSourcePlugin(i.type, i.id, i.pluginDefinition);
});
setIsDataSourcePluginRegistered(true);
@@ -153,6 +163,8 @@ const AppEditor = React.memo(() => {
setIsDataSourcePluginRegistered,
setShowAppSnapshot,
dispatch,
currentPage,
pageSize
]);

useEffect(() => {
@@ -219,6 +231,13 @@ const AppEditor = React.memo(() => {

return (
<ErrorBoundary fallback={fallbackUI}>
{/*<PaginationComp*/}
{/* currentPage={currentPage}*/}
{/* pageSize={pageSize}*/}
{/* setPageSize={setPageSize}*/}
{/* setCurrentPage={setCurrentPage}*/}
{/* total={elements.total}*/}
{/*/>*/}
{showAppSnapshot ? (
<Suspense fallback={<EditorSkeletonView />}>
<AppSnapshot
50 changes: 40 additions & 10 deletions client/packages/lowcoder/src/pages/queryLibrary/LeftNav.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import {useEffect, useState} from "react";
import styled, { css } from "styled-components";
import {
BluePlusIcon,
@@ -21,6 +21,7 @@ import { trans } from "i18n";
import { DatasourceType } from "@lowcoder-ee/constants/queryConstants";
import { saveAs } from "file-saver";
import DataSourceIcon from "components/DataSourceIcon";
import PaginationComp from "@lowcoder-ee/util/pagination/Pagination";

const Wrapper = styled.div<{ $readOnly?: boolean }>`
display: flex;
@@ -72,7 +73,7 @@ const CreateBtn = styled(TacoButton)<{ $readOnly?: boolean }>`
`;

const Body = styled.div`
height: calc(100% - 80px);
height: calc(100% - 120px);
display: flex;
flex-direction: column;
`;
@@ -158,11 +159,31 @@ export const LeftNav = (props: {
addQuery: () => void;
onSelect: (queryId: string) => void;
readOnly?: boolean;
setCurrentPage: (page: number) => void;
setPageSize: (size: number) => void;
currentPage: number;
pageSize: number;
total: number;
setSearchValues: any;
searchValues: string;
setModify?: any;
modify?: boolean;
}) => {
const {currentPage, setCurrentPage, pageSize, setPageSize, total , setSearchValues, searchValues, modify, setModify} = props
const dispatch = useDispatch();
const [searchValue, setSearchValue] = useState("");
const datasourceTypes = useSelector(getDataSourceTypesMap);

useEffect(()=> {
const timer = setTimeout(() => {
if (searchValue.length > 2 || searchValue === "")
setSearchValues(searchValue)
}, 500);
return () => clearTimeout(timer);
}, [searchValue])



return (
<ReadOnlyMask readOnly={!!props.readOnly}>
<Wrapper $readOnly={props.readOnly}>
@@ -189,12 +210,6 @@ export const LeftNav = (props: {
let datasourceTypeName =
datasourceTypes[q.libraryQueryDSL?.query?.compType as DatasourceType]?.name ??
"";
if (searchValue) {
return (
q.name.toLowerCase().includes(searchValue) ||
datasourceTypeName.toLowerCase().includes(searchValue)
);
}
return true;
})
.map((q) => (
@@ -234,8 +249,12 @@ export const LeftNav = (props: {
CustomModal.confirm({
title: trans("queryLibrary.deleteQueryTitle"),
content: trans("queryLibrary.deleteQueryContent"),
onConfirm: () =>
dispatch(deleteQueryLibrary({ queryLibraryId: q.id })),
onConfirm: () => {
dispatch(deleteQueryLibrary({ queryLibraryId: q.id }))
setTimeout(() => {
setModify(!modify);
}, 200);
},
confirmBtnType: "delete",
okText: trans("delete"),
}),
@@ -272,6 +291,17 @@ export const LeftNav = (props: {
</ScrollBar>
</SelectListWrapper>
</Body>
<PaginationComp
height={40}
marginTop={0}
marginBottom={0}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
pageSize={pageSize}
setPageSize={setPageSize}
total={total}
simple={true}
/>
</Wrapper>
</ReadOnlyMask>
);
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ import { useCompInstance } from "../../comps/utils/useCompInstance";
import { QueryLibraryComp } from "../../comps/comps/queryLibrary/queryLibraryComp";
import { useSearchParam, useThrottle } from "react-use";
import { Comp } from "lowcoder-core";
import { LibraryQuery } from "../../api/queryLibraryApi";
import {LibraryQuery} from "../../api/queryLibraryApi";
import { NameGenerator } from "../../comps/utils";
import { QueryLibraryHistoryView } from "./QueryLibraryHistoryView";
import { default as Form } from "antd/es/form";
@@ -46,6 +46,7 @@ import { importQueryLibrary } from "./importQueryLibrary";
import { registryDataSourcePlugin } from "constants/queryConstants";
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
import { Helmet } from "react-helmet";
import {fetchQLPaginationByOrg} from "@lowcoder-ee/util/pagination/axios";

const Wrapper = styled.div`
display: flex;
@@ -59,9 +60,21 @@ const RightContent = styled.div`
position: relative;
`;

interface ElementsState {
elements: LibraryQuery[];
total: number;
}

function transformData(input: LibraryQuery[]) {
const output: any = {};
input.forEach(item => {
output[item.id] = item;
});
return output;
}

export const QueryLibraryEditor = () => {
const dispatch = useDispatch();
const queryLibrary = useSelector(getQueryLibrary);
const queryLibraryRecords = useSelector(getQueryLibraryRecords);
const originDatasourceInfo = useSelector(getDataSource);
const currentUser = useSelector(getUser);
@@ -74,6 +87,12 @@ export const QueryLibraryEditor = () => {
const [publishModalVisible, setPublishModalVisible] = useState(false);
const [showHistory, setShowHistory] = useState(false);
const [isDataSourceReady, setIsDataSourceReady] = useState(false);
const [elements, setElements] = useState<ElementsState>({ elements: [], total: 0 });
const [queryLibrary, setQueryLibrary] = useState<any>({});
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [searchValues, setSearchValues] = useState("");
const [modify, setModify] = useState(false);

const selectedRecords = queryLibraryRecords[selectedQuery] ?? {};
const libraryQuery = queryLibrary[selectedQuery];
@@ -98,10 +117,33 @@ export const QueryLibraryEditor = () => {
const [comp, container] = useCompInstance(params);
useSaveQueryLibrary(libraryQuery, comp);

useEffect(() => {
try {
fetchQLPaginationByOrg(
{
name: searchValues,
pageNum: currentPage,
pageSize: pageSize,
}
).then(result => {
if (result.success){
setElements({elements: result.data || [], total: result.total || 1})
setQueryLibrary(transformData(result.data || []));
}
});
} catch (error) {
console.error(error)
}
}, [currentPage, pageSize, searchValues, modify])

useEffect( () => {
if (searchValues !== "")
setCurrentPage(1);
}, [searchValues]
);

useEffect(() => {
if (orgId) {
dispatch(fetchQueryLibrary());
dispatch(fetchDataSourceTypes({ organizationId: orgId }));
dispatch(
fetchDatasource({
organizationId: orgId,
@@ -125,7 +167,8 @@ export const QueryLibraryEditor = () => {

useEffect(() => {
if (!forwardQueryId && !queryLibrary[selectedQuery]) {
setSelectedQuery(Object.values(queryLibrary)?.[0]?.id);
// @ts-ignore
setSelectedQuery(Object.values(queryLibrary)?.[0]?.id);
}
}, [dispatch, Object.keys(queryLibrary).length]);

@@ -145,13 +188,13 @@ export const QueryLibraryEditor = () => {
})
.map((info) => info.datasource);

const recentlyUsed = Object.values(queryLibrary)
.map((i) => i.libraryQueryDSL?.query.datasourceId)
const recentlyUsed = Object.values(queryLibrary)
.map((i: any) => i.libraryQueryDSL?.query.datasourceId)
.map((id) => datasource.find((d) => d.id === id))
.filter((i) => !!i) as Datasource[];

const nameGenerator = new NameGenerator();
nameGenerator.init(Object.values(queryLibrary).map((t) => t.name));
nameGenerator.init(Object.values(queryLibrary).map((t: any) => t.name));
const newName = nameGenerator.genItemName(trans("queryLibrary.unnamed"));

const handleAdd = (type: BottomResTypeEnum, extraInfo?: any) => {
@@ -170,6 +213,11 @@ export const QueryLibraryEditor = () => {
},
(resp) => {
setSelectedQuery(resp.data.data.id);
setTimeout(() => {
setModify(!modify);
}, 200);
setCurrentPage(Math.ceil(elements.total / pageSize));

},
() => {}
)
@@ -189,7 +237,16 @@ export const QueryLibraryEditor = () => {
setSelectedQuery(id);
showCreatePanel(false);
} }
readOnly={showHistory} />
setCurrentPage={setCurrentPage}
setPageSize={setPageSize}
currentPage={currentPage}
pageSize={pageSize}
total={elements.total}
setSearchValues={setSearchValues}
searchValues={searchValues}
setModify={setModify}
modify={modify}
/>
<RightContent>
{!selectedQuery || !comp?.children.query.children.id.getView() ? (
EmptyQueryWithoutTab
@@ -202,6 +259,8 @@ export const QueryLibraryEditor = () => {
comp.propertyView({
onPublish: () => setPublishModalVisible(true),
onHistoryShow: () => setShowHistory(true),
setModify: setModify,
modify: modify
})
)}

@@ -219,6 +278,10 @@ export const QueryLibraryEditor = () => {
onSuccess: (resp) => {
setSelectedQuery(resp.data.data.id);
showCreatePanel(false);
setTimeout(() => {
setModify(!modify);
}, 200);
setCurrentPage(Math.ceil(elements.total / pageSize));
},
})} />
)}
Original file line number Diff line number Diff line change
@@ -31,8 +31,10 @@ function AddGroupUserDialog(props: {
orgUsersFetching: boolean;
groupUsers: GroupUser[];
style?: CSSProperties;
setModify?: any;
modify?: boolean
}) {
const { orgId, orgUsers, orgUsersFetching, groupUsers, groupId } = props;
const { orgId, orgUsers, orgUsersFetching, groupUsers, groupId, setModify, modify } = props;
const groupUserIdMap = new Map(groupUsers.map((gUser) => [gUser.userId, gUser]));
const [dialogVisible, setDialogVisible] = useState(false);
const addableUsers = orgUsers.filter((user) => !groupUserIdMap.has(user.userId));
@@ -83,6 +85,9 @@ function AddGroupUserDialog(props: {
}
}
dispatch(fetchGroupUsersAction({ groupId }));
setTimeout(() => {
setModify(!modify);
}, 200);
setDialogVisible(false);
}}
>
Original file line number Diff line number Diff line change
@@ -3,16 +3,13 @@ import { User } from "constants/userConstants";
import { AddIcon, ArrowIcon, CustomSelect, PackUpIcon, SuperUserIcon } from "lowcoder-design";
import { trans } from "i18n";
import ProfileImage from "pages/common/profileImage";
import React, { useEffect, useMemo } from "react";
import { connect, useDispatch } from "react-redux";
import { AppState } from "redux/reducers";
import React, { useMemo } from "react";
import { useDispatch } from "react-redux";
import {
deleteGroupUserAction,
fetchGroupUsersAction,
quitGroupAction,
updateUserGroupRoleAction,
} from "redux/reduxActions/orgActions";
import { getUser } from "redux/selectors/usersSelectors";
import styled from "styled-components";
import { formatTimestamp } from "util/dateTimeUtils";
import { currentOrgAdmin, isGroupAdmin } from "util/permissionUtils";
@@ -44,14 +41,15 @@ type GroupPermissionProp = {
group: OrgGroup;
orgId: string;
groupUsers: GroupUser[];
groupUsersFetching: boolean;
currentUserGroupRole: string;
currentUser: User;
setModify?: any;
modify?: boolean;
};

function GroupUsersPermission(props: GroupPermissionProp) {
const { Column } = TableStyled;
const { group, orgId, groupUsersFetching, groupUsers, currentUserGroupRole, currentUser } = props;
const { group, orgId, groupUsers, currentUserGroupRole, currentUser , setModify, modify} = props;
const adminCount = groupUsers.filter((user) => isGroupAdmin(user.role)).length;
const sortedGroupUsers = useMemo(() => {
return [...groupUsers].sort((a, b) => {
@@ -65,9 +63,6 @@ function GroupUsersPermission(props: GroupPermissionProp) {
});
}, [groupUsers]);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchGroupUsersAction({ groupId: group.groupId }));
}, []);
return (
<>
<PermissionHeaderWrapper>
@@ -85,6 +80,8 @@ function GroupUsersPermission(props: GroupPermissionProp) {
groupUsers={groupUsers}
orgId={orgId}
groupId={group.groupId}
setModify={setModify}
modify={modify}
trigger={
<AddMemberButton buttonType="primary" icon={<StyledAddIcon />}>
{trans("memberSettings.addMember")}
@@ -100,7 +97,7 @@ function GroupUsersPermission(props: GroupPermissionProp) {
dataSource={sortedGroupUsers}
rowKey="userId"
pagination={false}
loading={groupUsersFetching}
loading={groupUsers.length === 0}
>
<Column
title={trans("memberSettings.nameColumn")}
@@ -147,6 +144,9 @@ function GroupUsersPermission(props: GroupPermissionProp) {
groupId: group.groupId,
})
);
setTimeout(() => {
setModify(!modify);
}, 200);
}}
>
{TacoRoles.map((role) => (
@@ -177,6 +177,9 @@ function GroupUsersPermission(props: GroupPermissionProp) {
dispatch(
quitGroupAction({ groupId: group.groupId, userId: currentUser.id })
);
setTimeout(() => {
setModify(!modify);
}, 200);
}}
>
{trans("memberSettings.exitGroup")}
@@ -192,6 +195,9 @@ function GroupUsersPermission(props: GroupPermissionProp) {
groupId: group.groupId,
})
);
setTimeout(() => {
setModify(!modify);
}, 200);
}}
>
{trans("memberSettings.moveOutGroup")}
@@ -208,13 +214,4 @@ function GroupUsersPermission(props: GroupPermissionProp) {
);
}

const mapStateToProps = (state: AppState) => {
return {
groupUsers: state.ui.org.groupUsers,
groupUsersFetching: state.ui.org.groupUsersFetching,
currentUser: getUser(state),
currentUserGroupRole: state.ui.org.currentUserGroupRole,
};
};

export default connect(mapStateToProps)(GroupUsersPermission);
export default GroupUsersPermission;
13 changes: 8 additions & 5 deletions client/packages/lowcoder/src/pages/setting/permission/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { Route, Switch } from "react-router";
import React, {useState} from "react";
import { Route, Switch } from "react-router-dom";
import PermissionList from "./permissionList";
import PermissionDetail from "./permissionDetail";
import { PERMISSION_SETTING, PERMISSION_SETTING_DETAIL, SETTING_URL } from "constants/routesURL";

export default () => {
export default function PermissionRoutes() {
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
return (
<Switch>
<Route path={[SETTING_URL, PERMISSION_SETTING]} component={PermissionList} exact />
<Route path={PERMISSION_SETTING_DETAIL} component={PermissionDetail} />
<Route path={[SETTING_URL, PERMISSION_SETTING]} exact render={(props) => <PermissionList currentPage={currentPage} pageSize={pageSize} setCurrentPage={setCurrentPage} setPageSize={setPageSize} />} />
<Route path={PERMISSION_SETTING_DETAIL} render={(props) => <PermissionDetail currentPageProp={currentPage} pageSizeProp={pageSize} />} />
</Switch>
);
};
}
Original file line number Diff line number Diff line change
@@ -14,16 +14,13 @@ import {
import { trans, transToNode } from "i18n";
import InviteDialog from "pages/common/inviteDialog";
import ProfileImage from "pages/common/profileImage";
import React, { useEffect, useMemo } from "react";
import { connect, useDispatch, useSelector } from "react-redux";
import { AppState } from "redux/reducers";
import React, { useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
deleteOrgUserAction,
fetchOrgUsersAction,
quitOrgAction,
updateUserOrgRoleAction,
} from "redux/reduxActions/orgActions";
import { getUser } from "redux/selectors/usersSelectors";
import styled from "styled-components";
import { formatTimestamp } from "util/dateTimeUtils";
import { currentOrgAdmin } from "util/permissionUtils";
@@ -58,13 +55,14 @@ const StyledMembersIcon = styled(MembersIcon)`
type UsersPermissionProp = {
orgId: string;
orgUsers: OrgUser[];
orgUsersFetching: boolean;
currentUser: User;
setModify?: any;
modify?: boolean;
};

function OrgUsersPermission(props: UsersPermissionProp) {
const { Column } = TableStyled;
const { orgId, orgUsers, orgUsersFetching, currentUser } = props;
const { orgId, orgUsers, currentUser , setModify, modify} = props;
const adminCount = orgUsers.filter(
(user) => user.role === ADMIN_ROLE || user.role === SUPER_ADMIN_ROLE,
).length;
@@ -82,9 +80,9 @@ function OrgUsersPermission(props: UsersPermissionProp) {
});
}, [orgUsers]);

useEffect(() => {
dispatch(fetchOrgUsersAction(orgId));
}, [dispatch, orgId]);
// useEffect(() => {
// dispatch(fetchOrgUsersAction(orgId));
// }, [dispatch, orgId]);

const onResetPass = (userId: string) => {
return UserApi.resetPassword(userId)
@@ -151,7 +149,7 @@ function OrgUsersPermission(props: UsersPermissionProp) {
dataSource={sortedOrgUsers}
rowKey="userId"
pagination={false}
loading={orgUsersFetching}
loading={orgUsers.length === 0}
>
<Column
title={trans("memberSettings.nameColumn")}
@@ -279,6 +277,9 @@ function OrgUsersPermission(props: UsersPermissionProp) {
orgId: orgId,
})
);
setTimeout(() => {
setModify(!modify);
}, 200);
},
confirmBtnType: "delete",
okText: trans("memberSettings.moveOutOrg"),
@@ -299,12 +300,4 @@ function OrgUsersPermission(props: UsersPermissionProp) {
);
}

const mapStateToProps = (state: AppState) => {
return {
orgUsersFetching: state.ui.org.orgUsersFetching,
orgUsers: state.ui.org.orgUsers,
currentUser: getUser(state),
};
};

export default connect(mapStateToProps)(OrgUsersPermission);
export default OrgUsersPermission;
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchGroupsAction } from "redux/reduxActions/orgActions";
import React, {useEffect, useState} from "react";
import { useSelector } from "react-redux";
import { getUser } from "redux/selectors/usersSelectors";
import styled from "styled-components";
import GroupPermission from "./groupUsersPermission";
import UsersPermission from "./orgUsersPermission";
import { getOrgGroups } from "redux/selectors/orgSelectors";
import { useParams } from "react-router";
import { useParams } from "react-router-dom";
import {fetchGroupUsrPagination, fetchOrgGroups, fetchOrgUsrPagination} from "@lowcoder-ee/util/pagination/axios";
import PaginationComp from "@lowcoder-ee/util/pagination/Pagination";
import {OrgGroup} from "@lowcoder-ee/constants/orgConstants";

const PermissionContent = styled.div`
display: flex;
@@ -18,34 +19,109 @@ const PermissionContent = styled.div`
width: 100%;
`;

const All_Users = "users";
export default function PermissionSetting(props: {currentPageProp: number, pageSizeProp: number}) {

export default function PermissionSetting() {
const {currentPageProp, pageSizeProp} = props;
const user = useSelector(getUser);
const [elements, setElements] = useState<any>({ elements: [], total: 1, role: "" });
const [group, setGrouop] = useState<OrgGroup>();
const [orgMemberElements, setOrgMemberElements] = useState<any>({ elements: [], total: 1 })
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [modify, setModify] = useState(false);

const orgId = user.currentOrgId;
const orgGroups = useSelector(getOrgGroups);
const groupIdMap = new Map(orgGroups.map((group) => [group.groupId, group]));
const dispatch = useDispatch();
const currentUser = useSelector(getUser);
const selectKey = useParams<{ groupId: string }>().groupId;
useEffect(() => {
if (!orgId) {
return;

useEffect( () => {
fetchOrgGroups(
{
pageNum: currentPageProp,
pageSize: pageSizeProp,
}
).then(result => {
if (result.success && !!result.data){
setGrouop(result.data.find(group => group.groupId === selectKey))
}
else
console.error("ERROR: fetchFolderElements", result.error)
})
}, [currentPageProp, pageSizeProp]
)

useEffect( () => {
if (selectKey !== "users" && selectKey)
fetchGroupUsrPagination(
{
groupId:selectKey,
pageNum: currentPage,
pageSize: pageSize,
}
).then(result => {
if (result.success){
setElements({elements: result.data || [], total: result.total || 1, role: result.visitorRole || ""})
}
else
console.error("ERROR: fetchFolderElements", result.error)
}
)
else
{
fetchOrgUsrPagination(
{
orgId: orgId,
pageNum: currentPage,
pageSize: pageSize,
}
).then(result => {
if (result.success){
setOrgMemberElements({elements: result.data || [], total: result.total || 1})
}
else
console.error("ERROR: fetchFolderElements", result.error)
}
)
}
dispatch(fetchGroupsAction(orgId));
}, [orgId]);
},
[currentPage, pageSize, modify, selectKey]
)

if (!orgId) {
return null;
}

return (
<PermissionContent key={selectKey}>
{selectKey === All_Users ? (
<UsersPermission orgId={orgId} />
) : (
groupIdMap.has(selectKey) && (
<GroupPermission group={groupIdMap.get(selectKey)!} orgId={orgId} />
)
)}
</PermissionContent>
<PermissionContent key={selectKey}>
{selectKey === "users" ? (
<>
<UsersPermission
orgId={orgId}
// orgUsers={!orgMemberElements.elements.members ? [] : orgMemberElements.elements.members}
orgUsers={orgMemberElements.elements}
currentUser={currentUser}
setModify={setModify}
modify={modify}
/>
<PaginationComp setCurrentPage={setCurrentPage} setPageSize={setPageSize} currentPage={currentPage} pageSize={pageSize} total={orgMemberElements.total} />
</>
) : (
group && (
<>
<GroupPermission
group={group}
orgId={orgId}
groupUsers={elements.elements}
currentUserGroupRole={elements.role}
currentUser={currentUser}
setModify={setModify}
modify={modify}
/>
<PaginationComp setCurrentPage={setCurrentPage} setPageSize={setPageSize} currentPage={currentPage} pageSize={pageSize} total={elements.total} />
</>

)
)}
</PermissionContent>
);
}
}
Original file line number Diff line number Diff line change
@@ -21,7 +21,6 @@ import {
} from "lowcoder-design";
import styled from "styled-components";
import { trans } from "i18n";
import { getOrgGroups } from "redux/selectors/orgSelectors";
import { Table } from "components/Table";
import history from "util/history";
import { Level1SettingPageContentWithList, Level1SettingPageTitleWithBtn } from "../styled";
@@ -32,6 +31,8 @@ import { OrgGroup } from "constants/orgConstants";
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
import InviteDialog from "pages/common/inviteDialog";
import { Flex } from "antd";
import {fetchOrgGroups} from "@lowcoder-ee/util/pagination/axios";
import PaginationComp from "@lowcoder-ee/util/pagination/Pagination";

const NEW_GROUP_PREFIX = trans("memberSettings.newGroupPrefix");

@@ -51,23 +52,58 @@ type DataItemInfo = {
group?: OrgGroup;
};

export default function PermissionSetting() {
type PermissionSettingProps = {
currentPage: number;
setCurrentPage: (value: number) => void;
pageSize: number;
setPageSize: (value: number) => void;
};

interface ElementsState {
elements: OrgGroup[];
total: number;
}

export default function PermissionSetting(props: PermissionSettingProps) {

const {currentPage, setCurrentPage, pageSize, setPageSize} = props;
let dataSource: DataItemInfo[] = [];
const user = useSelector(getUser);
const orgId = user.currentOrgId;
const orgGroups = useSelector(getOrgGroups);
const visibleOrgGroups = orgGroups.filter((g) => !g.allUsersGroup);
const allUsersGroup = orgGroups.find((g) => g.allUsersGroup);
const dispatch = useDispatch();
const [needRenameId, setNeedRenameId] = useState<string | undefined>(undefined);
const { nameSuffixFunc, menuItemsFunc, menuExtraView } = usePermissionMenuItems(orgId);
const [groupCreating, setGroupCreating] = useState(false);
const [elements, setElements] = useState<ElementsState>({ elements: [], total: 0 });
const [modify, setModify] = useState(false);
const visibleOrgGroups = elements.elements.filter((g) => !g.allUsersGroup);
const allUsersGroup = elements.elements.find((g) => g.allUsersGroup);

useEffect(() => {
if (!orgId) {
return;
}
dispatch(fetchGroupsAction(orgId));
}, [orgId]);
useEffect( () => {
fetchOrgGroups(
{
pageNum: currentPage,
pageSize: pageSize,
}
).then(result => {
if (result.success){
setElements({elements: result.data || [], total: result.total || 1})
}
else
console.error("ERROR: fetchFolderElements", result.error)
})
}, [currentPage, pageSize, modify]
)


dataSource = currentPage === 1 ? [{
key: "users",
label: trans("memberSettings.allMembers"),
createTime: allUsersGroup?.createTime,
lock: true,
del: false,
rename: false,
}] : [];
if (!orgId) {
return null;
}
@@ -84,6 +120,9 @@ export default function PermissionSetting() {
setTimeout(() => {
dispatch(fetchGroupsAction(orgId));
}, 200);
setTimeout(() => {
setModify(!modify);
}, 200);
}
})
.catch((e) => {
@@ -98,24 +137,16 @@ export default function PermissionSetting() {
.then((resp) => {
if (validateResponse(resp)) {
dispatch(fetchGroupsAction(orgId));
setTimeout(() => {
setModify(!modify);
}, 200);
}
})
.catch((e) => {
messageInstance.error(e.message);
});
};

const dataSource: DataItemInfo[] = [
{
key: "users",
label: trans("memberSettings.allMembers"),
createTime: allUsersGroup?.createTime,
lock: true,
del: false,
rename: false,
},
];

visibleOrgGroups.forEach((group) => {
dataSource.push({
key: group.groupId,
@@ -180,6 +211,9 @@ export default function PermissionSetting() {
return;
}
dispatch(updateGroupAction(record.key, { groupName: value }, orgId));
setTimeout(() => {
setModify(!modify);
}, 200);
setNeedRenameId(undefined);
},
}}
@@ -255,6 +289,13 @@ export default function PermissionSetting() {
/>
</div>
{menuExtraView}
<PaginationComp
currentPage={currentPage}
pageSize={pageSize}
setPageSize={setPageSize}
setCurrentPage={setCurrentPage}
total={elements.total}
/>
</Level1SettingPageContentWithList>
);
}
55 changes: 44 additions & 11 deletions client/packages/lowcoder/src/pages/userAuth/formLoginSteps.tsx
Original file line number Diff line number Diff line change
@@ -2,8 +2,6 @@ import { FormInput, messageInstance, PasswordInput } from "lowcoder-design";
import {
AuthBottomView,
ConfirmButton,
FormWrapperMobile,
LoginCardTitle,
StyledRouteLink,
} from "pages/userAuth/authComponents";
import React, { useContext, useEffect, useState } from "react";
@@ -15,7 +13,7 @@ import { UserConnectionSource } from "@lowcoder-ee/constants/userConstants";
import { trans } from "i18n";
import { AuthContext, useAuthSubmit } from "pages/userAuth/authUtils";
import { ThirdPartyAuth } from "pages/userAuth/thirdParty/thirdPartyAuth";
import { AUTH_FORGOT_PASSWORD_URL, AUTH_REGISTER_URL, ORG_AUTH_FORGOT_PASSWORD_URL, ORG_AUTH_REGISTER_URL } from "constants/routesURL";
import { AUTH_FORGOT_PASSWORD_URL, AUTH_REGISTER_URL } from "constants/routesURL";
import { Link, useLocation, useParams } from "react-router-dom";
import { Divider } from "antd";
import Flex from "antd/es/flex";
@@ -27,8 +25,9 @@ import LeftOutlined from "@ant-design/icons/LeftOutlined";
import { fetchConfigAction } from "@lowcoder-ee/redux/reduxActions/configActions";
import { useDispatch, useSelector } from "react-redux";
import history from "util/history";
import ApplicationApi from "@lowcoder-ee/api/applicationApi";
import { getServerSettings } from "@lowcoder-ee/redux/selectors/applicationSelector";
import {fetchOrgPaginationByEmail} from "@lowcoder-ee/util/pagination/axios";
import PaginationComp from "@lowcoder-ee/util/pagination/Pagination";

const StyledCard = styled.div<{$selected: boolean}>`
display: flex;
@@ -91,6 +90,11 @@ type FormLoginProps = {
organizationId?: string;
}

interface ElementsState {
elements: any;
total: number;
}

export default function FormLoginSteps(props: FormLoginProps) {
const dispatch = useDispatch();
const location = useLocation();
@@ -111,6 +115,22 @@ export default function FormLoginSteps(props: FormLoginProps) {
const [skipWorkspaceStep, setSkipWorkspaceStep] = useState<boolean>(false);
const [signupEnabled, setSignupEnabled] = useState<boolean>(true);
const serverSettings = useSelector(getServerSettings);
const [elements, setElements] = useState<ElementsState>({ elements: [], total: 0 });
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);

useEffect(() => {
if (account)
fetchOrgPaginationByEmail({
email: account,
pageNum: currentPage,
pageSize: pageSize
}).then( result => {
setElements({elements: result.data || [], total: result.total || 1})
setOrgList(result.data)
}
)
}, [pageSize, currentPage])

useEffect(() => {
const { LOWCODER_EMAIL_SIGNUP_ENABLED } = serverSettings;
@@ -147,20 +167,25 @@ export default function FormLoginSteps(props: FormLoginProps) {
}

setOrgLoading(true);
OrgApi.fetchOrgsByEmail(account)
fetchOrgPaginationByEmail({
email: account,
pageNum: currentPage,
pageSize: pageSize
})
.then((resp) => {
if (validateResponse(resp)) {
setOrgList(resp.data.data);
if (!resp.data.data.length) {
if (resp.success) {
setElements({elements: resp.data || [], total: resp.total || 1})
setOrgList(resp.data);
if (!resp.data.length) {
history.push(
AUTH_REGISTER_URL,
{...location.state || {}, email: account},
)
return;
}
if (resp.data.data.length === 1) {
setOrganizationId(resp.data.data[0].orgId);
dispatch(fetchConfigAction(resp.data.data[0].orgId));
if (resp.data.length === 1) {
setOrganizationId(resp.data[0].orgId);
dispatch(fetchConfigAction(resp.data[0].orgId));
setCurrentStep(CurrentStepEnum.AUTH_PROVIDERS);
return;
}
@@ -233,6 +258,14 @@ export default function FormLoginSteps(props: FormLoginProps) {
{org.orgName}
</StyledCard>
))}
{orgList.length > 10 ?
<PaginationComp
currentPage={currentPage}
pageSize={pageSize}
setPageSize={setPageSize}
setCurrentPage={setCurrentPage}
total={elements.total}
/> : <></>}
</AccountLoginWrapper>
</>
)
9 changes: 8 additions & 1 deletion client/packages/lowcoder/src/util/homeResUtils.tsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,12 @@ import {
NavDocIcon,
} from "lowcoder-design";
import { HomeResTypeEnum } from "../types/homeRes";
import { APPLICATION_VIEW_URL, APPLICATION_MARKETPLACE_VIEW_URL, buildFolderUrl } from "../constants/routesURL";
import {
APPLICATION_VIEW_URL,
APPLICATION_MARKETPLACE_VIEW_URL,
buildFolderUrl,
ALL_APPLICATIONS_URL
} from "../constants/routesURL";
import history from "./history";
import { trans } from "../i18n";
import { FunctionComponent } from "react";
@@ -62,3 +67,5 @@ export const handleAppViewClick = (id: string) => window.open(APPLICATION_VIEW_U
export const handleMarketplaceAppViewClick = (id: string, isLocalMarketplace?: boolean) => isLocalMarketplace == true ? window.open(APPLICATION_VIEW_URL(id, "view_marketplace"), '_blank') : window.open(APPLICATION_MARKETPLACE_VIEW_URL(id, "view_marketplace"), '_blank');

export const handleFolderViewClick = (id: string) => history.push(buildFolderUrl(id));

export const backFolderViewClick = () => history.push(ALL_APPLICATIONS_URL);
86 changes: 86 additions & 0 deletions client/packages/lowcoder/src/util/pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import styled from "styled-components";
import { Pagination } from "antd";

interface PaginationLayoutProps {
height?: number;
marginTop?: number;
marginBottom?: number;
}

const PaginationLayout = styled(Pagination)<PaginationLayoutProps>`
display: flex;
justify-content: center;
align-items: center;
margin-top: ${(props) => props.marginTop !== undefined ? props.marginTop : 40}px !important;
margin-bottom: ${(props) => props.marginBottom !== undefined ? props.marginBottom : 20}px !important;
height: ${(props) => props.height}px;
`;

interface PaginationCompProps {
setCurrentPage: (page: number) => void;
setPageSize: (size: number) => void;
currentPage: number;
pageSize: number;
total: number;
height?: number;
marginTop?: number;
marginBottom?: number;
simple?: boolean;
}

const PaginationComp = (props: PaginationCompProps) => {
const {
setCurrentPage,
setPageSize,
currentPage,
pageSize,
total,
height,
marginTop,
marginBottom,
simple,
} = props;

const handlePageChange = (page: number, pageSize: number | undefined) => {
if (setCurrentPage) {
setCurrentPage(page);
}
};

const handlePageSizeChange = (current: number, size: number) => {
if (setPageSize) {
setPageSize(size);
}
};

return (
<>
{simple ?
<PaginationLayout
height={height}
marginTop={marginTop}
marginBottom={marginBottom}
current={currentPage}
pageSize={pageSize}
onChange={handlePageChange}
onShowSizeChange={handlePageSizeChange}
total={total}
simple
/> :
<PaginationLayout
height={height}
marginTop={marginTop}
marginBottom={marginBottom}
current={currentPage}
pageSize={pageSize}
onChange={handlePageChange}
onShowSizeChange={handlePageSizeChange}
total={total}
showSizeChanger
/>
}
</>
);
};

export default PaginationComp;
170 changes: 170 additions & 0 deletions client/packages/lowcoder/src/util/pagination/axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { FolderApi } from "@lowcoder-ee/api/folderApi";
import ApplicationApi from "@lowcoder-ee/api/applicationApi";
import {
fetchAppRequestType, fetchDataSourcePaginationRequestType,
fetchDBRequestType,
fetchFolderRequestType,
fetchGroupUserRequestType, fetchOrgsByEmailRequestType,
fetchOrgUserRequestType, fetchQueryLibraryPaginationRequestType,
orgGroupRequestType
} from "@lowcoder-ee/util/pagination/type";
import OrgApi from "@lowcoder-ee/api/orgApi";
import { DatasourceApi } from "@lowcoder-ee/api/datasourceApi";
import {QueryLibraryApi} from "@lowcoder-ee/api/queryLibraryApi";

export const fetchFolderElements = async (request: fetchFolderRequestType) => {
try {
const response = await FolderApi.fetchFolderElementsPagination(request);
return {
success: true,
data: response.data.data,
total:response.data.total
};
} catch (error) {
console.error('Failed to fetch data:', error);
return {
success: false,
error: error
};
}
}

export const fetchApplicationElements = async (request: fetchAppRequestType)=> {
try {
const response = await ApplicationApi.fetchAllApplicationsPagination(request);
return {
success: true,
data: response.data.data,
total: response.data.total
}
} catch (error: any) {
console.error('Failed to fetch data:', error);
return {
success: false,
error: error
};
}
}

export const fetchOrgGroups = async (request: orgGroupRequestType) => {
try{
const response = await OrgApi.fetchGroupPagination(request);
return {
success: true,
data:response.data.data,
total:response.data.total
}
}
catch (error: any) {
console.error('Failed to fetch data:', error);
return {
success: false,
error: error
};
}
}

export const fetchDatasourcePagination = async (request: fetchDBRequestType)=> {
try {
const response = await DatasourceApi.fetchDatasourcePaginationByOrg(request);
return {
success: true,
data: response.data.data,
total: response.data.total
}
} catch (error: any) {
console.error('Failed to fetch data:', error);
return {
success: false,
error: error
};
}
}

export const fetchGroupUsrPagination = async (request: fetchGroupUserRequestType)=> {
try {
const response = await OrgApi.fetchGroupUsersPagination(request);
return {
success: true,
data: response.data.data.members,
total: response.data.data.total,
visitorRole: response.data.data.visitorRole
}
} catch (error: any) {
console.error('Failed to fetch data:', error);
return {
success: false,
error: error
};
}
}

export const fetchOrgUsrPagination = async (request: fetchOrgUserRequestType)=> {
try {
const response = await OrgApi.fetchOrgUsersPagination(request);
return {
success: true,
data: response.data.data.members,
total: response.data.data.total,
}
} catch (error: any) {
console.error('Failed to fetch data:', error);
return {
success: false,
error: error
};
}
}

export const fetchQLPaginationByOrg = async (request: fetchQueryLibraryPaginationRequestType)=> {
try {
const response = await QueryLibraryApi.fetchQueryLibraryPaginationByOrg(request);
return {
success: true,
data: response.data.data,
total: response.data.total
}
} catch (error: any) {
console.error('Failed to fetch data:', error);
return {
success: false,
error: error
};
}
}

export const fetchJsDSPaginationByApp = async (request: fetchDataSourcePaginationRequestType)=> {
try {
const response = await DatasourceApi.fetchJsDatasourcePaginationByApp(request);
return {
success: true,
data: response.data.data,
total: response.data.total
}
} catch (error: any) {
console.error('Failed to fetch data:', error);
return {
success: false,
error: error
};
}
}



export const fetchOrgPaginationByEmail = async (request: fetchOrgsByEmailRequestType)=> {
try {
const response = await OrgApi.fetchOrgsPaginationByEmail(request);
return {
success: true,
data: response.data.data,
total: response.data.total
}
} catch (error: any) {
console.error('Failed to fetch data:', error);
return {
success: false,
error: error
};
}
}
107 changes: 107 additions & 0 deletions client/packages/lowcoder/src/util/pagination/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import {GroupUser, OrgUser} from "@lowcoder-ee/constants/orgConstants";

type ApplicationType = {
[key: number]: string; // This allows numeric indexing
};

export const ApplicationPaginationType: ApplicationType = {
0: "",
1: "APPLICATION",
2: "MODULE",
3: "NAVLAYOUT",
4: "FOLDER",
6: "MOBILETABLAYOUT",
7: "COMPOUND_APPLICATION",
};

export interface GenericApiPaginationResponse<T> {
total: number;
success: boolean;
code: number;
message: string;
data: T;
}
export interface GroupUsersPaginationResponse {
success: boolean;
data: {
members: GroupUser[];
visitorRole: string;
total: number;
};
}

export interface OrgUsersPaginationResponse {
success: boolean;
data: {
total: number;
members: OrgUser[];
visitorRole: string;
};
}

export type ApiPaginationResponse = {
total: number;
success: boolean;
code: number;
message: string;
data: any;
};


export interface fetchAppRequestType {
pageNum?: number;
pageSize?: number;
name?: string;
applicationType?: number;
}

export interface fetchFolderRequestType {
id?: string;
pageNum?: number;
pageSize?: number;
name?: string;
applicationType?: string;
}

export interface fetchDBRequestType {
orgId: string;
pageNum?: number;
pageSize?: number;
name?: string;
type?: string;
}

export interface orgGroupRequestType{
pageNum?: number;
pageSize?: number;
}
export interface fetchOrgUserRequestType {
orgId: string;
pageNum?: number;
pageSize?: number;
}

export interface fetchGroupUserRequestType {
groupId: string;
pageNum?: number;
pageSize?: number;
}

export interface fetchQueryLibraryPaginationRequestType {
name?: string;
pageNum?: number;
pageSize?: number;
}

export interface fetchDataSourcePaginationRequestType {
appId: string;
name?: string;
pageNum?: number;
pageSize?: number;
}

export interface fetchOrgsByEmailRequestType {
email: string;
pageNum?: number;
pageSize?: number;
}
6 changes: 3 additions & 3 deletions server/node-service/yarn.lock
Original file line number Diff line number Diff line change
@@ -8424,8 +8424,8 @@ __metadata:
linkType: hard

"node-gyp@npm:latest":
version: 10.2.0
resolution: "node-gyp@npm:10.2.0"
version: 10.3.1
resolution: "node-gyp@npm:10.3.1"
dependencies:
env-paths: ^2.2.0
exponential-backoff: ^3.1.1
@@ -8439,7 +8439,7 @@ __metadata:
which: ^4.0.0
bin:
node-gyp: bin/node-gyp.js
checksum: 0233759d8c19765f7fdc259a35eb046ad86c3d09e22f7384613ae2b89647dd27fcf833fdf5293d9335041e91f9b1c539494225959cdb312a5c8080b7534b926f
checksum: 91b0690ab504fe051ad66863226dc5ecac72b8471f85e8428e4d5ca3217d3a2adfffae48cd555e8d009a4164689fff558b88d2bc9bfd246452a3336ab308cf99
languageName: node
linkType: hard