From 5c1ef4d5d57e610de449a1848d619c08509fe0ab Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Tue, 20 Feb 2024 16:37:17 +0500 Subject: [PATCH 01/10] added api_usage endpoint --- client/packages/lowcoder/src/api/orgApi.ts | 9 +++++++ .../src/constants/reduxActionConstants.ts | 2 ++ .../lowcoder/src/pages/setting/index.tsx | 10 ++++++++ .../redux/reducers/uiReducers/orgReducer.ts | 10 ++++++++ .../src/redux/reduxActions/orgActions.ts | 22 ++++++++++++++++ .../lowcoder/src/redux/sagas/orgSagas.ts | 25 ++++++++++++++++++- .../src/redux/selectors/orgSelectors.ts | 4 +++ 7 files changed, 81 insertions(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/api/orgApi.ts b/client/packages/lowcoder/src/api/orgApi.ts index 31ed40e58..7c1396a39 100644 --- a/client/packages/lowcoder/src/api/orgApi.ts +++ b/client/packages/lowcoder/src/api/orgApi.ts @@ -29,6 +29,10 @@ export interface CreateOrgResponse extends ApiResponse { data: { orgId: string }; } +export interface OrgAPIUsageResponse extends ApiResponse { + data: number; +} + export class OrgApi extends Api { static createGroupURL = "/v1/groups"; static updateGroupURL = (groupId: string) => `/v1/groups/${groupId}/update`; @@ -47,6 +51,7 @@ export class OrgApi extends Api { static createOrgURL = "/v1/organizations"; static deleteOrgURL = (orgId: string) => `/v1/organizations/${orgId}`; static updateOrgURL = (orgId: string) => `/v1/organizations/${orgId}/update`; + static fetchUsage = (orgId: string) => `/v1/organizations/${orgId}/api-usage`; static createGroup(request: { name: string }): AxiosPromise> { return Api.post(OrgApi.createGroupURL, request); @@ -127,6 +132,10 @@ export class OrgApi extends Api { static updateOrg(request: UpdateOrgPayload): AxiosPromise { return Api.put(OrgApi.updateOrgURL(request.id), request); } + + static fetchAPIUsage(orgId: string, lastMonthOnly?: boolean): AxiosPromise { + return Api.get(OrgApi.fetchUsage(orgId), lastMonthOnly); + } } export default OrgApi; diff --git a/client/packages/lowcoder/src/constants/reduxActionConstants.ts b/client/packages/lowcoder/src/constants/reduxActionConstants.ts index 7b2b28442..fdf34040b 100644 --- a/client/packages/lowcoder/src/constants/reduxActionConstants.ts +++ b/client/packages/lowcoder/src/constants/reduxActionConstants.ts @@ -100,6 +100,8 @@ export const ReduxActionTypes = { UPDATE_USER_PROFILE_SUCCESS: "UPDATE_USER_PROFILE_SUCCESS", UPLOAD_USER_HEAD_SUCCESS: "UPLOAD_USER_HEAD_SUCCESS", // update avatar MARK_USER_STATUS: "MARK_USER_STATUS", + FETCH_ORG_API_USAGE: "FETCH_ORG_API_USAGE", + FETCH_ORG_API_USAGE_SUCCESS: "FETCH_ORG_API_USAGE_SUCCESS", /* home data */ FETCH_HOME_DATA: "FETCH_HOME_DATA", diff --git a/client/packages/lowcoder/src/pages/setting/index.tsx b/client/packages/lowcoder/src/pages/setting/index.tsx index e77bb1450..9cfa2b235 100644 --- a/client/packages/lowcoder/src/pages/setting/index.tsx +++ b/client/packages/lowcoder/src/pages/setting/index.tsx @@ -8,6 +8,16 @@ import SettingHome from "./settingHome"; export function Setting() { const user = useSelector(getUser); + + /* fetch Org's API usage + + const apiUsage = useSelector(getOrgApiUsage); + useEffect(() => { + dispatch(fetchAPIUsageAction(user.currentOrgId)); + }, [user.currentOrgId]) + + */ + if (!currentOrgAdminOrDev(user)) { history.push(BASE_URL); } diff --git a/client/packages/lowcoder/src/redux/reducers/uiReducers/orgReducer.ts b/client/packages/lowcoder/src/redux/reducers/uiReducers/orgReducer.ts index 2d35b7e0c..f020cf99f 100644 --- a/client/packages/lowcoder/src/redux/reducers/uiReducers/orgReducer.ts +++ b/client/packages/lowcoder/src/redux/reducers/uiReducers/orgReducer.ts @@ -8,6 +8,7 @@ import { User } from "constants/userConstants"; import { DeleteOrgUserPayload, GroupUsersPayload, + OrgAPIUsagePayload, OrgUsersPayload, RemoveGroupUserPayload, } from "redux/reduxActions/orgActions"; @@ -24,6 +25,7 @@ const initialState: OrgReduxState = { groupUsersFetching: true, fetchOrgGroupsFinished: false, orgCreateStatus: "init", + apiUsage: 0, }; const orgReducer = createImmerReducer(initialState, { @@ -104,6 +106,13 @@ const orgReducer = createImmerReducer(initialState, { ...state, orgCreateStatus: "error", }), + [ReduxActionTypes.FETCH_ORG_API_USAGE_SUCCESS]: ( + state: OrgReduxState, + action: ReduxAction + ): OrgReduxState => ({ + ...state, + apiUsage: action.payload.apiUsage, + }) }); export interface OrgReduxState { @@ -115,6 +124,7 @@ export interface OrgReduxState { groupUsersFetching: boolean; fetchOrgGroupsFinished: boolean; orgCreateStatus: ApiRequestStatus; + apiUsage: number; } export default orgReducer; diff --git a/client/packages/lowcoder/src/redux/reduxActions/orgActions.ts b/client/packages/lowcoder/src/redux/reduxActions/orgActions.ts index d7326c1d7..9d2f3eb6a 100644 --- a/client/packages/lowcoder/src/redux/reduxActions/orgActions.ts +++ b/client/packages/lowcoder/src/redux/reduxActions/orgActions.ts @@ -151,3 +151,25 @@ export const updateOrgSuccess = (payload: UpdateOrgPayload) => { payload: payload, }; }; + +export type OrgAPIUsagePayload = { + apiUsage: number, +}; + +export const fetchAPIUsageAction = ( + orgId: string, + lastMonthOnly?: boolean, +) => ({ + type: ReduxActionTypes.FETCH_ORG_API_USAGE, + payload: { + orgId, + lastMonthOnly, + }, +}); + +export const fetchAPIUsageSuccessAction = (apiUsage: number) => ({ + type: ReduxActionTypes.FETCH_ORG_API_USAGE_SUCCESS, + payload: { + apiUsage, + }, +}); diff --git a/client/packages/lowcoder/src/redux/sagas/orgSagas.ts b/client/packages/lowcoder/src/redux/sagas/orgSagas.ts index 8835a2f3f..f1604241d 100644 --- a/client/packages/lowcoder/src/redux/sagas/orgSagas.ts +++ b/client/packages/lowcoder/src/redux/sagas/orgSagas.ts @@ -1,7 +1,7 @@ import { messageInstance } from "lowcoder-design"; import { ApiResponse, GenericApiResponse } from "api/apiResponses"; -import OrgApi, { CreateOrgResponse, GroupUsersResponse, OrgUsersResponse } from "api/orgApi"; +import OrgApi, { CreateOrgResponse, GroupUsersResponse, OrgAPIUsageResponse, OrgUsersResponse } from "api/orgApi"; import { AxiosResponse } from "axios"; import { OrgGroup } from "constants/orgConstants"; import { @@ -280,6 +280,28 @@ export function* updateOrgSaga(action: ReduxAction) { } } +export function* fetchAPIUsageSaga(action: ReduxAction<{ + orgId: string, + lastMonthOnly?: boolean, +}>) { + try { + const response: AxiosResponse = yield call( + OrgApi.fetchAPIUsage, + action.payload.orgId, + action.payload.lastMonthOnly, + ); + const isValidResponse: boolean = validateResponse(response); + if (isValidResponse) { + yield put({ + type: ReduxActionTypes.FETCH_ORG_API_USAGE_SUCCESS, + payload: response.data.data, + }); + } + } catch (error) { + log.error(error); + } +} + export default function* orgSagas() { yield all([ takeLatest(ReduxActionTypes.UPDATE_GROUP_INFO, updateGroupSaga), @@ -297,5 +319,6 @@ export default function* orgSagas() { takeLatest(ReduxActionTypes.CREATE_ORG, createOrgSaga), takeLatest(ReduxActionTypes.DELETE_ORG, deleteOrgSaga), takeLatest(ReduxActionTypes.UPDATE_ORG, updateOrgSaga), + takeLatest(ReduxActionTypes.FETCH_ORG_API_USAGE, fetchAPIUsageSaga), ]); } diff --git a/client/packages/lowcoder/src/redux/selectors/orgSelectors.ts b/client/packages/lowcoder/src/redux/selectors/orgSelectors.ts index 281ff138b..8ea9aa3ec 100644 --- a/client/packages/lowcoder/src/redux/selectors/orgSelectors.ts +++ b/client/packages/lowcoder/src/redux/selectors/orgSelectors.ts @@ -15,3 +15,7 @@ export const getFetchOrgGroupsFinished = (state: AppState) => { export const getOrgCreateStatus = (state: AppState) => { return state.ui.org.orgCreateStatus; }; + +export const getOrgApiUsage = (state: AppState) => { + return state.ui.org.apiUsage; +} From f808455179381a2b34b1567cba243185f6fb6542 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 21 Feb 2024 18:37:30 +0500 Subject: [PATCH 02/10] added marketplace endpoints --- .../lowcoder/src/api/applicationApi.ts | 22 ++++++++++++++++++- .../src/constants/reduxActionConstants.ts | 2 ++ .../reducers/uiReducers/applicationReducer.ts | 9 ++++++++ .../redux/reduxActions/applicationActions.ts | 4 ++++ .../src/redux/sagas/applicationSagas.ts | 22 +++++++++++++++++++ .../redux/selectors/applicationSelector.ts | 2 ++ 6 files changed, 60 insertions(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/api/applicationApi.ts b/client/packages/lowcoder/src/api/applicationApi.ts index 41ee23204..06659a441 100644 --- a/client/packages/lowcoder/src/api/applicationApi.ts +++ b/client/packages/lowcoder/src/api/applicationApi.ts @@ -78,6 +78,7 @@ class ApplicationApi extends Api { static newURLPrefix = "/applications"; static fetchHomeDataURL = "/v1/applications/home"; static createApplicationURL = "/v1/applications"; + static fetchAllMarketplaceAppsURL = "/v1/applications/marketplace-apps"; static deleteApplicationURL = (applicationId: string) => `/v1/applications/${applicationId}`; static getAppPublishInfoURL = (applicationId: string) => `/v1/applications/${applicationId}/view`; static getAppEditingInfoURL = (applicationId: string) => `/v1/applications/${applicationId}`; @@ -92,6 +93,9 @@ class ApplicationApi extends Api { `/v1/applications/${applicationId}/permissions/${permissionId}`; static createFromTemplateURL = `/v1/applications/createFromTemplate`; static publicToAllURL = (applicationId: string) => `/applications/${applicationId}/public-to-all`; + static publicToMarketplaceURL = (applicationId: string) => `/v1/applications/${applicationId}/public-to-marketplace`; + static getMarketplaceAppURL = (applicationId: string) => `/v1/applications/${applicationId}/view_marketplace`; + static fetchHomeData(request: HomeDataPayload): AxiosPromise { return Api.get(ApplicationApi.fetchHomeDataURL, request); @@ -167,7 +171,9 @@ class ApplicationApi extends Api { const url = type === "published" ? ApplicationApi.getAppPublishInfoURL(applicationId) - : ApplicationApi.getAppEditingInfoURL(applicationId); + : type === "view_marketplace" + ? ApplicationApi.getMarketplaceAppURL(applicationId) + : ApplicationApi.getAppEditingInfoURL(applicationId); return Api.get(url); } @@ -211,6 +217,20 @@ class ApplicationApi extends Api { publicToAll: publicToAll, }); } + + static publicToMarketplace(appId: string, publicToMarketplace: boolean) { + return Api.put(ApplicationApi.publicToMarketplaceURL(appId), { + publicToMarketplace, + }); + } + + static fetchAllMarketplaceApps() { + return Api.get(ApplicationApi.fetchAllMarketplaceAppsURL); + } + + static getMarketplaceApp(appId: string) { + return Api.get(ApplicationApi.getMarketplaceAppURL(appId)); + } } export default ApplicationApi; diff --git a/client/packages/lowcoder/src/constants/reduxActionConstants.ts b/client/packages/lowcoder/src/constants/reduxActionConstants.ts index fdf34040b..ae7ebf129 100644 --- a/client/packages/lowcoder/src/constants/reduxActionConstants.ts +++ b/client/packages/lowcoder/src/constants/reduxActionConstants.ts @@ -137,6 +137,8 @@ export const ReduxActionTypes = { FETCH_ALL_APPLICATIONS_SUCCESS: "FETCH_ALL_APPLICATIONS_SUCCESS", FETCH_ALL_MODULES_INIT: "FETCH_ALL_MODULES_INIT", FETCH_ALL_MODULES_SUCCESS: "FETCH_ALL_MODULES_SUCCESS", + FETCH_ALL_MARKETPLACE_APPS: "FETCH_ALL_MARKETPLACE_APPS", + FETCH_ALL_MARKETPLACE_APPS_SUCCESS: "FETCH_ALL_MARKETPLACE_APPS_SUCCESS", /* user profile */ SET_USER_PROFILE_SETTING_MODAL_VISIBLE: "SET_USER_PROFILE_SETTING_MODAL_VISIBLE", diff --git a/client/packages/lowcoder/src/redux/reducers/uiReducers/applicationReducer.ts b/client/packages/lowcoder/src/redux/reducers/uiReducers/applicationReducer.ts index 7938543a4..dda424c1f 100644 --- a/client/packages/lowcoder/src/redux/reducers/uiReducers/applicationReducer.ts +++ b/client/packages/lowcoder/src/redux/reducers/uiReducers/applicationReducer.ts @@ -24,6 +24,7 @@ const initialState: ApplicationReduxState = { applicationList: [], modules: [], recycleList: [], + marketplace: [], loadingStatus: { isFetchingHomeData: false, fetchHomeDataFinished: false, @@ -98,6 +99,13 @@ const usersReducer = createReducer(initialState, { ...state, recycleList: action.payload, }), + [ReduxActionTypes.FETCH_ALL_MARKETPLACE_APPS_SUCCESS]: ( + state: ApplicationReduxState, + action: ReduxAction + ): ApplicationReduxState => ({ + ...state, + marketplace: action.payload, + }), [ReduxActionTypes.CREATE_APPLICATION_INIT]: ( state: ApplicationReduxState ): ApplicationReduxState => ({ @@ -336,6 +344,7 @@ export interface ApplicationReduxState { applicationList: ApplicationMeta[]; modules: ApplicationMeta[]; recycleList: ApplicationMeta[]; + marketplace: ApplicationMeta[]; appPermissionInfo?: AppPermissionInfo; currentApplication?: ApplicationMeta; templateId?: string; diff --git a/client/packages/lowcoder/src/redux/reduxActions/applicationActions.ts b/client/packages/lowcoder/src/redux/reduxActions/applicationActions.ts index 12697e352..7619798d6 100644 --- a/client/packages/lowcoder/src/redux/reduxActions/applicationActions.ts +++ b/client/packages/lowcoder/src/redux/reduxActions/applicationActions.ts @@ -32,6 +32,10 @@ export const fetchApplicationRecycleList = () => ({ type: ReduxActionTypes.FETCH_APPLICATION_RECYCLE_LIST_INIT, }); +export const fetchAllMarketplaceApps = () => ({ + type: ReduxActionTypes.FETCH_ALL_MARKETPLACE_APPS, +}); + export type CreateApplicationPayload = { applicationName: string; applicationType: AppTypeEnum; diff --git a/client/packages/lowcoder/src/redux/sagas/applicationSagas.ts b/client/packages/lowcoder/src/redux/sagas/applicationSagas.ts index c49644182..6967c3c14 100644 --- a/client/packages/lowcoder/src/redux/sagas/applicationSagas.ts +++ b/client/packages/lowcoder/src/redux/sagas/applicationSagas.ts @@ -372,6 +372,24 @@ function* fetchApplicationRecycleListSaga() { } } +function* fetchAllMarketplaceAppsSaga() { + try { + const response: AxiosResponse> = yield call( + ApplicationApi.fetchAllMarketplaceApps + ); + const isValidResponse: boolean = validateResponse(response); + if (isValidResponse) { + yield put({ + type: ReduxActionTypes.FETCH_ALL_MARKETPLACE_APPS_SUCCESS, + payload: response.data.data, + }); + } + } catch (error: any) { + messageInstance.error(error.message); + log.debug("fetch marketplace apps error: ", error); + } +} + export default function* applicationSagas() { yield all([ takeLatest(ReduxActionTypes.FETCH_HOME_DATA, fetchHomeDataSaga), @@ -393,5 +411,9 @@ export default function* applicationSagas() { ReduxActionTypes.FETCH_APPLICATION_RECYCLE_LIST_INIT, fetchApplicationRecycleListSaga ), + takeLatest( + ReduxActionTypes.FETCH_ALL_MARKETPLACE_APPS, + fetchAllMarketplaceAppsSaga, + ), ]); } diff --git a/client/packages/lowcoder/src/redux/selectors/applicationSelector.ts b/client/packages/lowcoder/src/redux/selectors/applicationSelector.ts index 3279f65c6..2d888a9c9 100644 --- a/client/packages/lowcoder/src/redux/selectors/applicationSelector.ts +++ b/client/packages/lowcoder/src/redux/selectors/applicationSelector.ts @@ -8,6 +8,8 @@ export const modulesSelector = (state: AppState): ApplicationMeta[] => state.ui. export const recycleListSelector = (state: AppState) => state.ui.application.recycleList; +export const marketplaceSelector = (state: AppState) => state.ui.application.marketplace; + export const getHomeOrg = (state: AppState) => state.ui.application.homeOrg; export const isFetchingHomeData = (state: AppState) => From 5bce8d21259d0aaaadaea3e0f22c6b1cde51d2a2 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 21 Feb 2024 18:39:52 +0500 Subject: [PATCH 03/10] added switch to make as public to marketplace --- .../PermissionDialog/AppPermissionDialog.tsx | 21 +++++++++++++++++++ .../src/constants/applicationConstants.ts | 6 ++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx b/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx index b86282d80..37ec80ab6 100644 --- a/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx +++ b/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx @@ -197,12 +197,33 @@ function AppShareView(props: { }) { const { applicationId, permissionInfo, isModule } = props; const [isPublic, setPublic] = useState(permissionInfo.publicToAll); + const [isPublicToMarketplace, setPublicToMarketplace] = useState(permissionInfo.publicToMarketplace); const dispatch = useDispatch(); useEffect(() => { setPublic(permissionInfo.publicToAll); }, [permissionInfo.publicToAll]); + useEffect(() => { + setPublicToMarketplace(permissionInfo.publicToMarketplace); + }, [permissionInfo.publicToMarketplace]); return (
+ + { + setPublicToMarketplace(checked); + ApplicationApi.publicToMarketplace(applicationId, checked) + .then((resp) => { + validateResponse(resp); + dispatch(updateAppPermissionInfo({ publicToMarketplace: checked })); + }) + .catch((e) => { + messageInstance.error(e.message); + }); + }} + label={isModule ? 'Public module to marketplace' : 'Public app to marketplace'} + /> + = { [AppTypeEnum.MobileTabLayout]: "mobileTabLayout", }; -export type ApplicationDSLType = "editing" | "published"; +export type ApplicationDSLType = "editing" | "published" | "view_marketplace"; export type ApplicationRoleType = "viewer" | "editor" | "owner"; export type ApplicationPermissionType = "USER" | "GROUP" | "ORG_ADMIN"; @@ -36,6 +36,7 @@ export interface ApplicationMeta { containerSize?: { height: number; width: number }; createBy: string; createAt: number; + creatorEmail?: string; orgId: string; role: ApplicationRoleType; extra: ApplicationExtra; @@ -80,9 +81,10 @@ export interface AppPermissionInfo { permissions: PermissionItem[]; invitationCodes: AppInviteInfo[]; publicToAll: boolean; + publicToMarketplace: boolean; } -export type AppViewMode = "edit" | "preview" | "view"; +export type AppViewMode = "edit" | "preview" | "view" | "view_marketplace"; export type AppPathParams = { viewMode: AppViewMode; From 3ff918e5f24ca80367054046aade47906d90daae Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 21 Feb 2024 18:41:38 +0500 Subject: [PATCH 04/10] added marketplace route --- .../src/icons/icon-application-home.svg | 4 +-- .../icon-application-marketplace-active.svg | 12 +++++++ .../icons/icon-application-marketplace.svg | 16 +++++++++ .../lowcoder-design/src/icons/index.ts | 2 ++ client/packages/lowcoder/src/app.tsx | 2 ++ .../lowcoder/src/constants/routesURL.ts | 1 + .../packages/lowcoder/src/i18n/locales/de.ts | 1 + .../packages/lowcoder/src/i18n/locales/en.ts | 1 + .../packages/lowcoder/src/i18n/locales/zh.ts | 1 + .../pages/ApplicationV2/MarketplaceView.tsx | 35 +++++++++++++++++++ .../src/pages/ApplicationV2/index.tsx | 17 +++++++++ 11 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 client/packages/lowcoder-design/src/icons/icon-application-marketplace-active.svg create mode 100644 client/packages/lowcoder-design/src/icons/icon-application-marketplace.svg create mode 100644 client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx diff --git a/client/packages/lowcoder-design/src/icons/icon-application-home.svg b/client/packages/lowcoder-design/src/icons/icon-application-home.svg index 8435068e1..e56d475c9 100644 --- a/client/packages/lowcoder-design/src/icons/icon-application-home.svg +++ b/client/packages/lowcoder-design/src/icons/icon-application-home.svg @@ -1,7 +1,7 @@ - + @@ -11,6 +11,6 @@ - + diff --git a/client/packages/lowcoder-design/src/icons/icon-application-marketplace-active.svg b/client/packages/lowcoder-design/src/icons/icon-application-marketplace-active.svg new file mode 100644 index 000000000..6db0975e5 --- /dev/null +++ b/client/packages/lowcoder-design/src/icons/icon-application-marketplace-active.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/client/packages/lowcoder-design/src/icons/icon-application-marketplace.svg b/client/packages/lowcoder-design/src/icons/icon-application-marketplace.svg new file mode 100644 index 000000000..417513063 --- /dev/null +++ b/client/packages/lowcoder-design/src/icons/icon-application-marketplace.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/packages/lowcoder-design/src/icons/index.ts b/client/packages/lowcoder-design/src/icons/index.ts index 938cd1fc4..e9d141765 100644 --- a/client/packages/lowcoder-design/src/icons/index.ts +++ b/client/packages/lowcoder-design/src/icons/index.ts @@ -176,11 +176,13 @@ export { ReactComponent as HomeModuleIcon } from "./icon-application-module.svg" export { ReactComponent as HomeQueryLibraryIcon } from "./icon-application-query-library.svg"; export { ReactComponent as HomeDataSourceIcon } from "./icon-application-datasource.svg"; export { ReactComponent as RecyclerIcon } from "./icon-application-recycler.svg"; +export { ReactComponent as MarketplaceIcon } from "./icon-application-marketplace.svg"; export { ReactComponent as HomeActiveIcon } from "./icon-application-home-active.svg"; export { ReactComponent as HomeModuleActiveIcon } from "./icon-application-module-active.svg"; export { ReactComponent as HomeQueryLibraryActiveIcon } from "./icon-application-query-library-active.svg"; export { ReactComponent as HomeDataSourceActiveIcon } from "./icon-application-datasource-active.svg"; export { ReactComponent as RecyclerActiveIcon } from "./icon-application-recycler-active.svg"; +export { ReactComponent as MarketplaceActiveIcon } from "./icon-application-marketplace-active.svg"; export { ReactComponent as FavoritesIcon } from "./icon-application-favorites.svg"; export { ReactComponent as HomeSettingIcon } from "./icon-application-setting.svg"; export { ReactComponent as FolderIcon } from "./icon-application-folder.svg"; diff --git a/client/packages/lowcoder/src/app.tsx b/client/packages/lowcoder/src/app.tsx index 0e25e3a52..8b3a944f7 100644 --- a/client/packages/lowcoder/src/app.tsx +++ b/client/packages/lowcoder/src/app.tsx @@ -14,6 +14,7 @@ import { IMPORT_APP_FROM_TEMPLATE_URL, INVITE_LANDING_URL, isAuthUnRequired, + MARKETPLACE_URL, ORG_AUTH_LOGIN_URL, ORG_AUTH_REGISTER_URL, QUERY_LIBRARY_URL, @@ -138,6 +139,7 @@ class AppIndex extends React.Component { FOLDER_URL, TRASH_URL, SETTING, + MARKETPLACE_URL, ]} // component={ApplicationListPage} component={ApplicationHome} diff --git a/client/packages/lowcoder/src/constants/routesURL.ts b/client/packages/lowcoder/src/constants/routesURL.ts index 8b7abb5ee..1c21036ab 100644 --- a/client/packages/lowcoder/src/constants/routesURL.ts +++ b/client/packages/lowcoder/src/constants/routesURL.ts @@ -21,6 +21,7 @@ export const ORGANIZATION_SETTING_DETAIL = `${ORGANIZATION_SETTING}/:orgId`; export const ALL_APPLICATIONS_URL = "/apps"; export const MODULE_APPLICATIONS_URL = "/apps/module"; +export const MARKETPLACE_URL = `/marketplace`; export const DATASOURCE_URL = `/datasource`; export const DATASOURCE_CREATE_URL = `${DATASOURCE_URL}/new/:datasourceType`; export const DATASOURCE_EDIT_URL = `${DATASOURCE_URL}/:datasourceId`; diff --git a/client/packages/lowcoder/src/i18n/locales/de.ts b/client/packages/lowcoder/src/i18n/locales/de.ts index b30fead70..defca3530 100644 --- a/client/packages/lowcoder/src/i18n/locales/de.ts +++ b/client/packages/lowcoder/src/i18n/locales/de.ts @@ -2053,6 +2053,7 @@ export const de = { "modules": "Module", "module": "Modul", "trash": "Papierkorb", + "marketplace": "Marktplatz", "queryLibrary": "Abfragebibliothek", "datasource": "Datenquellen", "selectDatasourceType": "Datenquellentyp auswählen", diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 74b2296b9..9d59f83df 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -2236,6 +2236,7 @@ export const en = { "modules": "Modules", "module": "Module", "trash": "Trash", + "marketplace": "Marketplace", "queryLibrary": "Query Library", "datasource": "Data Sources", "selectDatasourceType": "Select Data Source Type", diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index 28194e00f..b2df346d3 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -2120,6 +2120,7 @@ home: { modules: "模块", module: "模块", trash: "回收站", + marketplace: "市场", queryLibrary: "查询管理", datasource: "数据源", selectDatasourceType: "选择数据源类型", diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx new file mode 100644 index 000000000..d518250d1 --- /dev/null +++ b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx @@ -0,0 +1,35 @@ +import { useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { HomeLayout } from "./HomeLayout"; +import { MARKETPLACE_URL } from "constants/routesURL"; +import { marketplaceSelector } from "redux/selectors/applicationSelector"; +import { fetchAllMarketplaceApps } from "redux/reduxActions/applicationActions"; +import { trans } from "../../i18n"; + +export function MarketplaceView() { + const [haveFetchedApps, setHaveFetchApps] = useState(false); + + const dispatch = useDispatch(); + const marketplaceApps = useSelector(marketplaceSelector); + + useEffect(() => { + if (!marketplaceApps.length && !haveFetchedApps) { + dispatch(fetchAllMarketplaceApps()); + setHaveFetchApps(true); + } + }, []); + + useEffect(() => { + if (marketplaceApps.length) { + setHaveFetchApps(true); + } + }, [marketplaceApps]) + + return ( + + ); +}; \ No newline at end of file diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx index 7e24d5eee..a9525298b 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx @@ -4,6 +4,7 @@ import { FOLDER_URL, FOLDER_URL_PREFIX, FOLDERS_URL, + MARKETPLACE_URL, MODULE_APPLICATIONS_URL, QUERY_LIBRARY_URL, SETTING, @@ -30,6 +31,8 @@ import { PointIcon, RecyclerActiveIcon, RecyclerIcon, + MarketplaceIcon, + MarketplaceActiveIcon, } from "lowcoder-design"; import React, { useEffect, useState } from "react"; import { fetchAllApplications, fetchHomeData } from "redux/reduxActions/applicationActions"; @@ -44,6 +47,7 @@ import styled, { css } from "styled-components"; import history from "../../util/history"; 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"; @@ -411,6 +415,19 @@ export default function ApplicationHome() { visible: ({ user }) => user.orgDev, onSelected: (_, currentPath) => currentPath.split("/")[1] === "datasource", }, + { + text: {trans("home.marketplace")}, + routePath: MARKETPLACE_URL, + routePathExact: false, + routeComp: MarketplaceView, + icon: ({ selected, ...otherProps }) => + selected ? ( + + ) : ( + + ), + visible: ({ user }) => user.orgDev, + }, { text: {trans("settings.title")}, routePath: SETTING, From 606a9d17d797b8be35659decf27ac2cd2d156679 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 21 Feb 2024 18:42:44 +0500 Subject: [PATCH 05/10] update app editor to view marketplace app --- .../src/pages/ApplicationV2/HomeLayout.tsx | 18 +++--- .../src/pages/ApplicationV2/HomeResCard.tsx | 8 +++ .../src/pages/ApplicationV2/HomeTableView.tsx | 5 ++ .../src/pages/common/headerStartDropdown.tsx | 61 +++++++++++-------- .../src/pages/common/previewHeader.tsx | 20 +++++- .../lowcoder/src/pages/editor/AppEditor.tsx | 2 +- .../lowcoder/src/util/homeResUtils.tsx | 2 + client/packages/lowcoder/src/util/hooks.ts | 2 +- 8 files changed, 81 insertions(+), 37 deletions(-) diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx index fde596953..b18b944f7 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx @@ -238,11 +238,12 @@ export interface HomeRes { isEditable?: boolean; isManageable: boolean; isDeletable: boolean; + isMarketplace?: boolean; } export type HomeBreadcrumbType = { text: string; path: string }; -export type HomeLayoutMode = "view" | "trash" | "module" | "folder" | "folders"; +export type HomeLayoutMode = "view" | "trash" | "module" | "folder" | "folders" | "marketplace"; export interface HomeLayoutProps { breadcrumb?: HomeBreadcrumbType[]; @@ -306,11 +307,12 @@ export function HomeLayout(props: HomeLayoutProps) { id: e.applicationId, name: e.name, type: HomeResTypeEnum[HomeResTypeEnum[e.applicationType] as HomeResKey], - creator: e.createBy, + creator: e?.creatorEmail ?? e.createBy, lastModifyTime: e.lastModifyTime, - isEditable: canEditApp(user, e), - isManageable: canManageApp(user, e), - isDeletable: canEditApp(user, e), + isEditable: mode !== 'marketplace' && canEditApp(user, e), + isManageable: mode !== 'marketplace' && canManageApp(user, e), + isDeletable: mode !== 'marketplace' && canEditApp(user, e), + isMarketplace: mode === 'marketplace', } ); @@ -387,7 +389,7 @@ export function HomeLayout(props: HomeLayoutProps) { onChange={(e) => setSearchValue(e.target.value)} style={{ width: "192px", height: "32px", margin: "0" }} /> - {mode !== "trash" && user.orgDev && ( + {mode !== "trash" && mode !== "marketplace" && user.orgDev && ( )} @@ -421,11 +423,13 @@ export function HomeLayout(props: HomeLayoutProps) {
{mode === "trash" ? trans("home.trashEmpty") + : mode === "marketplace" + ? "No apps in marketplace yet" : user.orgDev ? trans("home.projectEmptyCanAdd") : trans("home.projectEmpty")}
- {mode !== "trash" && user.orgDev && } + {mode !== "trash" && mode !== "marketplace" && user.orgDev && } )} diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx index 26c97eb30..32e2258ed 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx @@ -11,6 +11,7 @@ import { handleAppEditClick, handleAppViewClick, handleFolderViewClick, + handleMarketplaceAppViewClick, HomeResInfo, } from "../../util/homeResUtils"; import { HomeResOptions } from "./HomeResOptions"; @@ -167,6 +168,7 @@ export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => voi )} { + console.log(res.isMarketplace); if (appNameEditing) { return; } @@ -177,6 +179,10 @@ export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => voi history.push(APPLICATION_VIEW_URL(res.id, "view")); return; } + if(res.isMarketplace) { + handleMarketplaceAppViewClick(res.id); + return; + } res.isEditable ? handleAppEditClick(e, res.id) : handleAppViewClick(res.id); } }} @@ -211,6 +217,8 @@ export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => voi onClick={() => res.type === HomeResTypeEnum.Folder ? handleFolderViewClick(res.id) + : res.isMarketplace + ? handleMarketplaceAppViewClick(res.id) : handleAppViewClick(res.id) } > diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeTableView.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeTableView.tsx index 4a76939ee..aa767250c 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeTableView.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeTableView.tsx @@ -7,6 +7,7 @@ import { handleAppEditClick, handleAppViewClick, handleFolderViewClick, + handleMarketplaceAppViewClick, HomeResInfo, } from "../../util/homeResUtils"; import { HomeResTypeEnum } from "../../types/homeRes"; @@ -75,6 +76,8 @@ export const HomeTableView = (props: { resources: HomeRes[] }) => { } 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); } @@ -209,6 +212,8 @@ export const HomeTableView = (props: { resources: HomeRes[] }) => { e.stopPropagation(); return item.type === HomeResTypeEnum.Folder ? handleFolderViewClick(item.id) + : item.isMarketplace + ? handleMarketplaceAppViewClick(item.id) : handleAppViewClick(item.id); }} style={{ marginRight: "52px" }} diff --git a/client/packages/lowcoder/src/pages/common/headerStartDropdown.tsx b/client/packages/lowcoder/src/pages/common/headerStartDropdown.tsx index eefa27800..1ea283690 100644 --- a/client/packages/lowcoder/src/pages/common/headerStartDropdown.tsx +++ b/client/packages/lowcoder/src/pages/common/headerStartDropdown.tsx @@ -11,7 +11,7 @@ import { } from "lowcoder-design"; import { trans, transToNode } from "i18n"; import { exportApplicationAsJSONFile } from "pages/ApplicationV2/components/AppImport"; -import { useContext, useState } from "react"; +import { useContext, useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { currentApplication } from "redux/selectors/applicationSelector"; import { showAppSnapshotSelector } from "redux/selectors/appSnapshotSelector"; @@ -23,6 +23,8 @@ import { recycleApplication } from "redux/reduxActions/applicationActions"; import { CopyModal } from "./copyModal"; import { ExternalEditorContext } from "util/context/ExternalEditorContext"; import { messageInstance } from "lowcoder-design"; +import { getUser } from "redux/selectors/usersSelectors"; +import { canEditApp } from "util/permissionUtils"; const PackUpIconStyled = styled(PackUpIcon)` transform: rotate(180deg); @@ -68,6 +70,7 @@ export const TypeName = { }; export function HeaderStartDropdown(props: { setEdit: () => void }) { + const user = useSelector(getUser); const showAppSnapshot = useSelector(showAppSnapshotSelector); const applicationId = useApplicationId(); const application = useSelector(currentApplication); @@ -76,6 +79,37 @@ export function HeaderStartDropdown(props: { setEdit: () => void }) { const { appType } = useContext(ExternalEditorContext); const isModule = appType === AppTypeEnum.Module; + const isEditable = canEditApp(user, application); + + const menuItems = useMemo(() => ([ + { + key: "edit", + label: {trans("header.editName")}, + visible: isEditable, + }, + { + key: "export", + label: {trans("header.export")}, + visible: true, + }, + { + key: "duplicate", + label: ( + + {trans("header.duplicate", { + type: TypeName[application?.applicationType!]?.toLowerCase(), + })} + + ), + visible: true, + }, + { + key: "delete", + label: {trans("home.moveToTrash")}, + visible: isEditable, + }, + ]), [isEditable]); + return ( <> void }) { }); } }} - items={[ - { - key: "edit", - label: {trans("header.editName")}, - }, - { - key: "export", - label: {trans("header.export")}, - }, - { - key: "duplicate", - label: ( - - {trans("header.duplicate", { - type: TypeName[application?.applicationType!]?.toLowerCase(), - })} - - ), - }, - { - key: "delete", - label: {trans("home.moveToTrash")}, - }, - ]} + items={menuItems.filter(item => item.visible)} /> )} > diff --git a/client/packages/lowcoder/src/pages/common/previewHeader.tsx b/client/packages/lowcoder/src/pages/common/previewHeader.tsx index 507330cbf..85ae198d6 100644 --- a/client/packages/lowcoder/src/pages/common/previewHeader.tsx +++ b/client/packages/lowcoder/src/pages/common/previewHeader.tsx @@ -17,6 +17,9 @@ import { Logo } from "@lowcoder-ee/assets/images"; import { AppPermissionDialog } from "../../components/PermissionDialog/AppPermissionDialog"; import { useState } from "react"; import { getBrandingConfig } from "../../redux/selectors/configSelectors"; +import { HeaderStartDropdown } from "./headerStartDropdown"; +import { useParams } from "react-router"; +import { AppPathParams } from "constants/applicationConstants"; const HeaderFont = styled.div<{ $bgColor: string }>` font-weight: 500; @@ -125,21 +128,32 @@ export function HeaderProfile(props: { user: User }) { } export const PreviewHeader = () => { + const params = useParams(); const user = useSelector(getUser); const application = useSelector(currentApplication); const applicationId = useApplicationId(); const templateId = useSelector(getTemplateId); const brandingConfig = useSelector(getBrandingConfig); const [permissionDialogVisible, setPermissionDialogVisible] = useState(false); + const isViewMarketplaceMode = params.viewMode === 'view_marketplace'; const headerStart = ( <> history.push(ALL_APPLICATIONS_URL)}> - - {application && application.name} - + {isViewMarketplaceMode && ( + { + + }} + /> + )} + {!isViewMarketplaceMode && ( + + {application && application.name} + + )} ); diff --git a/client/packages/lowcoder/src/pages/editor/AppEditor.tsx b/client/packages/lowcoder/src/pages/editor/AppEditor.tsx index 05d5c4a08..c1d241a0a 100644 --- a/client/packages/lowcoder/src/pages/editor/AppEditor.tsx +++ b/client/packages/lowcoder/src/pages/editor/AppEditor.tsx @@ -33,7 +33,7 @@ export default function AppEditor() { const isUserViewMode = useUserViewMode(); const params = useParams(); const applicationId = params.applicationId; - const viewMode = params.viewMode === "view" ? "published" : "editing"; + const viewMode = params.viewMode === "view" ? "published" : params.viewMode === "view_marketplace" ? "view_marketplace" : "editing"; const currentUser = useSelector(getUser); const dispatch = useDispatch(); const fetchOrgGroupsFinished = useSelector(getFetchOrgGroupsFinished); diff --git a/client/packages/lowcoder/src/util/homeResUtils.tsx b/client/packages/lowcoder/src/util/homeResUtils.tsx index 2c37817d9..8f7e1dfe8 100644 --- a/client/packages/lowcoder/src/util/homeResUtils.tsx +++ b/client/packages/lowcoder/src/util/homeResUtils.tsx @@ -58,4 +58,6 @@ export const handleAppEditClick = (e: any, id: string): void => { export const handleAppViewClick = (id: string) => window.open(APPLICATION_VIEW_URL(id, "view")); +export const handleMarketplaceAppViewClick = (id: string) => window.open(APPLICATION_VIEW_URL(id, "view_marketplace")); + export const handleFolderViewClick = (id: string) => history.push(buildFolderUrl(id)); diff --git a/client/packages/lowcoder/src/util/hooks.ts b/client/packages/lowcoder/src/util/hooks.ts index 67e397184..ca67a902f 100644 --- a/client/packages/lowcoder/src/util/hooks.ts +++ b/client/packages/lowcoder/src/util/hooks.ts @@ -25,7 +25,7 @@ export function isUserViewMode(params?: AppPathParams) { return false; } const { viewMode } = params; - return viewMode === "preview" || viewMode === "view"; + return viewMode === "preview" || viewMode === "view" || viewMode === "view_marketplace"; } /** From 61c166b0ee1a8aeabddda10566589d0d24317459 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 21 Feb 2024 18:49:34 +0500 Subject: [PATCH 06/10] checkbox/radio fix --- .../src/comps/comps/selectInputComp/segmentedControl.tsx | 1 - .../src/comps/comps/selectInputComp/selectInputConstants.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx index 6c82fd62b..156c83799 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx @@ -79,7 +79,6 @@ const SegmentedControlBasicComp = (function () { return new UICompBuilder(SegmentChildrenMap, (props) => { const [ validateState, - handleValidate, handleChange, ] = useSelectInputValidate(props); return props.label({ diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectInputConstants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectInputConstants.tsx index 7c4fdc126..c69c35605 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectInputConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectInputConstants.tsx @@ -91,7 +91,7 @@ export const useSelectInputValidate = (props: ValidationParams) => { return [ validateState, - handleValidate, + // handleValidate, handleChange, ] as const; }; From 82c3de8565f9febaceff5aafa1d27860928f94dd Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Thu, 22 Feb 2024 09:15:45 +0100 Subject: [PATCH 07/10] Fix Lowcocer Comps Documentation --- client/packages/lowcoder-comps/README.md | 105 ++++++++++++++++++-- client/packages/lowcoder-comps/package.json | 2 +- 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/client/packages/lowcoder-comps/README.md b/client/packages/lowcoder-comps/README.md index 7c74d491a..15f56dc46 100644 --- a/client/packages/lowcoder-comps/README.md +++ b/client/packages/lowcoder-comps/README.md @@ -1,22 +1,100 @@ -# lowcoder comp lib +# Lowcoder Extra Components + +This is the workspace for Lowcoder Extra Components like Calendar, Image Editor, Mermaid Charts and eCharts. + +## Local Development preparation + +Navigate your terminal or bash to your /root folder (lowcoder repository) to install Lowcoder Extra Components dependencies and the Lowcoder SDK + +To develop with the Lowcoder Extra Components after you clone the Lowcoder Repository, first make sure the Lowcoder SDK is local built. + +```bash +cd client/packages/lowcoder-sdk +yarn build +``` ## Start -Start dev server to develop your comp lib. +Now you can start the local dev server for Lowcoder Extra Components to develop and add your Component Plugin ```bash +cd client/packages/lowcoder-comps yarn start +``` -# or +The local dev server will build for roughly a minute and open then a Browser Window on http://localhost:9000/ with the Lowcoder Component Builder. + +## Local development + +After the local dev server is started, the Lowcoder Component Builder is prepared. A new browser window should open at http://localhost:9000 This is the Components Preview, which allows you to see your new component in action, as it would work in the Lowcoder Editor. + +Data, methods and properties are visible and interactive, so you can test your Component during development. The view will get automatically refreshed. + +The Lowcoder Component Builder makes the development & publishing of multiple individual components as bundle possible. Find the /src/comps folder in /lowcoder-comps. Here are existing components to find. It is suggested for new components to create a new folder. In the left navigation of the Components Preview you can switch between your components. + +to see your component and include it in the processing on the development server, you have to do the folloiwing steps: + +### modify /lowcoder-comps/package.json + +```JSON +"yournewcomponent": { + "name": "Your new Component name", + "icon": "./icons/your-icon.svg", + "description": "A Component Plugin to ...", + "category": "itemHandling", + "layoutInfo": { + "w": 6, + "h": 30 + } +} +``` -npm start + +Please choose one category out of: + + - dashboards + - layout + - forms + - collaboration + - projectmanagement + - scheduling + - documents + - itemHandling + - multimedia + - integration + +layoutInfo helps you to define the size (in grid-cells) of your Component in the grid for the very first moment, when a user drags your Component out of the components display on the right side in the Lowcoder Editor. + +### modify /lowcoder-comps/src/index.ts + +```JavaScript +Add your Component for the exported members of Lowcoder Extra Components + +import { ChartCompWithDefault } from "./comps/chartComp/chartComp"; +import { ImageEditorComp } from "./comps/imageEditorComp/index"; +import { CalendarComp } from "./comps/calendarComp/calendarComp"; +import { MermaidComp } from "comps/mermaidComp"; + +import { YourComponent } from "comps/yourComponentFolder/yourComponent"; + +export default { + chart: ChartCompWithDefault, + imageEditor: ImageEditorComp, + calendar: CalendarComp, + mermaid: MermaidComp, + + yourcomponent: YourComponent, +}; ``` +Now your Plugin should be visibe and displayed in the Lowcoder Component Builder at http://localhost:9000/ ## Build -Build current comp lib into a .tgz file that you can upload it to the Lowcoder Comp Market. +When you finish development and all tests, you can build the Components to use it in runtime. -Before build you should change the version in package.json file. +This will build the current Component Plugins into a .tgz file that you can upload. + +**Before build you should change the version in package.json file.** ```bash yarn build @@ -25,3 +103,18 @@ yarn build npm run build ``` + +## How to publish a Component Plugin + +With the following command you can publish the script to the NPM repository: + +```bash +yarn build --publish +``` + +This command will publis the whole Lowcoder Extra Components bundle to [NPMjs](https://www.npmjs.com/) +Make sure, you updated the Version of Lowcoder Comps before in /lowcoder-comps/package.json + +## Contribute your Plugin + +If you wish to contribute your plugin and persist it as general Lowcoder Extra Component, please raise a PR to our /dev branch in the Lowcoder Community-Edition Repository https://github.com/lowcoder-org/lowcoder \ No newline at end of file diff --git a/client/packages/lowcoder-comps/package.json b/client/packages/lowcoder-comps/package.json index 1bf1dae92..43386ccf9 100644 --- a/client/packages/lowcoder-comps/package.json +++ b/client/packages/lowcoder-comps/package.json @@ -69,7 +69,7 @@ }, "devDependencies": { "jest": "29.3.0", - "vite": "^4.5.2", + "vite": "^5.0.12", "vite-tsconfig-paths": "^3.6.0" } } From 4b94a7f138a1ac4814307b56cedaaa287578a1b9 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Thu, 22 Feb 2024 15:25:44 +0500 Subject: [PATCH 08/10] Fix for conditional color for table's column --- .../lowcoder/src/comps/comps/tableComp/tableCompView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx index 729e8f6b9..d19586c6b 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx @@ -517,7 +517,7 @@ function TableCellView(props: { columnTitle: title, }); const cellColor = cellColorFn({ - currentCell: record[title.toLowerCase()], + currentCell: record[title], }); const style = { From 8bb42424dc90bd54eed5d99be43e5b776643d700 Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Thu, 22 Feb 2024 17:47:09 +0100 Subject: [PATCH 09/10] Small adaptions for Marketplace --- .../comps/calendarComp/calendarConstants.tsx | 8 +- .../assets/images/marketplaceHeaderImage.jpg | Bin 0 -> 80436 bytes .../PermissionDialog/AppPermissionDialog.tsx | 40 +- .../packages/lowcoder/src/i18n/locales/de.ts | 8 +- .../packages/lowcoder/src/i18n/locales/en.ts | 8 +- .../packages/lowcoder/src/i18n/locales/zh.ts | 4 + .../src/pages/ApplicationV2/HomeLayout.tsx | 31 +- .../src/pages/ApplicationV2/HomeResCard.tsx | 1 - .../src/pages/ApplicationV2/index.tsx | 28 +- client/yarn.lock | 445 +++++++++++++++++- 10 files changed, 526 insertions(+), 47 deletions(-) create mode 100644 client/packages/lowcoder/src/assets/images/marketplaceHeaderImage.jpg diff --git a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx index 93c07c462..bbc02cafd 100644 --- a/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx +++ b/client/packages/lowcoder-comps/src/comps/calendarComp/calendarConstants.tsx @@ -205,9 +205,9 @@ export const Wrapper = styled.div<{ flex-direction: inherit; } .fc-day-today .fc-daygrid-day-number { - background-color: ${(props) => props.$theme.primary}; + background-color: ${(props) => props.$theme?.primary ? props.$theme.primary : props.$style.background}; color: ${(props) => - contrastText(props.$theme.primary || "", props.$theme.textDark, props.$theme.textLight)}; + contrastText(props.$theme?.primary || "", props.$theme?.textDark || "#000000", props.$theme?.textLight || "#ffffff")}; } .fc-daygrid-day-events { padding: 1px 0 5px 0; @@ -585,10 +585,10 @@ export const Wrapper = styled.div<{ } .fc-day-today.fc-col-header-cell { background-color: ${(props) => - isDarkColor(props.$style.background) ? "#ffffff19" : toHex(props.$theme.primary!) + "19"}; + isDarkColor(props.$style.background) ? "#ffffff19" : toHex(props.$theme?.primary!) + "19"}; a { color: ${(props) => - !isDarkColor(props.$style.background) && darkenColor(props.$theme.primary!, 0.1)}; + !isDarkColor(props.$style.background) && darkenColor(props.$theme?.primary!, 0.1)}; } } .fc-col-header-cell-cushion { diff --git a/client/packages/lowcoder/src/assets/images/marketplaceHeaderImage.jpg b/client/packages/lowcoder/src/assets/images/marketplaceHeaderImage.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a4eeb5714ca803778e37f8cf02b7b886f281cf9c GIT binary patch literal 80436 zcmbTdbzEG{)-JfafyP||1a}GU5Zoa+jk~)`a3{FCTaYF|f;$8Y?h-5^xRYQ(5;C3l z-0wSQX70T+zq|e@o-WyY)v8sso~qjX`TS=aLr+m!+EiUrO-}KZEC2!l0EWUVdly$Q z4gfg2czI~bOHt|R8&Dxn17H9ZfB;+oU~cK@rlKXM4ZuZFR+`EaUgG|OE(WM0061OYx7uHds@PABpf^Wdb$10{|?6l7LI>081XOmfGY^c z#DB5vzwnEH>ii2!{KZzzE>`e5e|2`Va<=-5hu}EI$J-i?5njP@w2!^DFB~tzF`bjQ zvppQ&!7+iewYet%AY%N@_p-LMgJVuO#_-VAl!oJ%0DyvF`ybfiKd_heYq*{OAnoer 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

_4vG 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

    DJ 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 literal 0 HcmV?d00001 diff --git a/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx b/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx index 37ec80ab6..499ceae37 100644 --- a/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx +++ b/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx @@ -29,6 +29,7 @@ import { StyledLoading } from "./commonComponents"; import { PermissionRole } from "./Permission"; import { SHARE_TITLE } from "../../constants/apiConstants"; import { messageInstance } from "lowcoder-design"; +import { Divider } from "antd"; export const AppPermissionDialog = (props: { applicationId: string; @@ -207,23 +208,7 @@ function AppShareView(props: { }, [permissionInfo.publicToMarketplace]); return (
    - - { - setPublicToMarketplace(checked); - ApplicationApi.publicToMarketplace(applicationId, checked) - .then((resp) => { - validateResponse(resp); - dispatch(updateAppPermissionInfo({ publicToMarketplace: checked })); - }) - .catch((e) => { - messageInstance.error(e.message); - }); - }} - label={isModule ? 'Public module to marketplace' : 'Public app to marketplace'} - /> - + + {isPublic && + + { + setPublicToMarketplace(checked); + ApplicationApi.publicToMarketplace(applicationId, checked) + .then((resp) => { + validateResponse(resp); + dispatch(updateAppPermissionInfo({ publicToMarketplace: checked })); + }) + .catch((e) => { + messageInstance.error(e.message); + }); + } } + label={isModule ? trans("home.moduleMarketplaceMessage") : trans("home.appMarketplaceMessage")} /> + } + { isPublicToMarketplace && <>
    + {trans("home.marketplaceGoodPublishing")} +
    } + {isPublic && }
    ); diff --git a/client/packages/lowcoder/src/i18n/locales/de.ts b/client/packages/lowcoder/src/i18n/locales/de.ts index defca3530..671abf94c 100644 --- a/client/packages/lowcoder/src/i18n/locales/de.ts +++ b/client/packages/lowcoder/src/i18n/locales/de.ts @@ -2111,8 +2111,12 @@ export const de = { "allPermissions": "Besitzer", "shareLink": "Link teilen: ", "copyLink": "Link kopieren", - "appPublicMessage": "Mach die App öffentlich. Jeder kann sie sehen.", - "modulePublicMessage": "Mach das Modul öffentlich. Jeder kann es sehen.", + "appPublicMessage": "App veröffentlichen. Auf dem Marktplatz für jeden zu sehen.", + "modulePublicMessage": "Module veröffentlichen. Auf dem Marktplatz für jeden zu sehen.", + "appMarketplaceMessage": "Veröffentlichen Sie Ihre App auf dem Lowcoder-Marktplatz. Jeder kann sie dort sehen und kopieren.", + "moduleMarketplaceMessage": "Veröffentlichen Sie Ihr Modul auf dem Lowcoder-Marktplatz. Jeder kann es dort sehen und kopieren.", + "marketplaceGoodPublishing": "Bitte stellen Sie sicher, dass Ihre App gut benannt und einfach zu bedienen ist. Entfernen Sie alle sensiblen Informationen vor der Veröffentlichung. Entfernen Sie außerdem lokale Datenquellen und ersetzen Sie sie durch statische, integrierte temporäre Daten.", + "noMarketplaceApps": "Noch sind keine Anwendungen auf dem Marktplatz.", "memberPermissionList": "Mitgliedschaftsberechtigungen: ", "orgName": "{orgName} admins", "addMember": "Mitglieder hinzufügen", diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 3d56617d2..a40d69d2e 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -2238,8 +2238,8 @@ export const en = { "history": "History" }, "home": { - "allApplications": "All Apps", - "allModules": "All Modules", + "allApplications": "Your Apps", + "allModules": "Your Modules", "allFolders": "All Folders", "modules": "Modules", "module": "Module", @@ -2304,6 +2304,10 @@ export const en = { "copyLink": "Copy link", "appPublicMessage": "Make the app public. Anyone can view.", "modulePublicMessage": "Make the module public. Anyone can view.", + "appMarketplaceMessage": "Publish your App on Lowcoder Marketplace. Anyone can view and copy it from there.", + "moduleMarketplaceMessage": "Publish your Module on Lowcoder Marketplace. Anyone can view and copy it from there.", + "marketplaceGoodPublishing": "Please make sure your app is well-named and easy to use. Remove any sensitive information before publishing. Also, remove local datasources and replace by static built-in temporary data.", + "noMarketplaceApps": "No apps yet in the marketplace", "memberPermissionList": "Member permissions: ", "orgName": "{orgName} admins", "addMember": "Add members", diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index b847a6090..b90d3a8f1 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -2183,6 +2183,10 @@ home: { copyLink: "复制链接", appPublicMessage: "将应用设为公开,任何人都可以查看.", modulePublicMessage: "将模块设为公开,任何人都可以查看.", + "appMarketplaceMessage": "发布您的应用程序到Lowcoder市场.任何人都可以在那里查看和复制它.", + "moduleMarketplaceMessage": "发布您的模块到Lowcoder市场.任何人都可以在那里查看和复制它.", + "marketplaceGoodPublishing": "请确保您的应用程序命名准确、易于使用。发布前请删除任何敏感信息。此外,移除本地数据源,代之以静态内置临时数据", + "noMarketplaceApps": "市场上还没有应用程序", memberPermissionList: "成员权限:", orgName: "{orgName}管理员", addMember: "添加成员", diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx index b18b944f7..9ea75b051 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx @@ -32,6 +32,7 @@ import { CreateDropdown } from "./CreateDropdown"; import { trans } from "../../i18n"; import { isFetchingFolderElements } from "../../redux/selectors/folderSelector"; import { checkIsMobile } from "util/commonUtils"; +import MarketplaceHeaderImage from "assets/images/marketplaceHeaderImage.jpg"; const Wrapper = styled.div` display: flex; @@ -65,6 +66,23 @@ const OperationWrapper = styled.div` } `; +const MarketplaceHeader = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + height: 200px; + padding: 0 36px; + margin: 8px 0 20px 0; + @media screen and (max-width: 500px) { + padding: 0 24px; + } + > img { + width: 100%; + object-fit: cover; + } +`; + const ContentWrapper = styled.div` position: relative; `; @@ -364,6 +382,10 @@ export function HomeLayout(props: HomeLayoutProps) { {showNewUserGuide(user) && } {/**/} + {mode === "marketplace" && ( + Lowcoder Application Marketplace + )} + {mode !== "folders" && mode !== "module" && ( node} suffixIcon={} @@ -396,6 +419,8 @@ export function HomeLayout(props: HomeLayoutProps) { + + {isFetching && resList.length === 0 ? ( ) : ( @@ -424,7 +449,7 @@ export function HomeLayout(props: HomeLayoutProps) { {mode === "trash" ? trans("home.trashEmpty") : mode === "marketplace" - ? "No apps in marketplace yet" + ? trans("home.noMarketplaceApps") : user.orgDev ? trans("home.projectEmptyCanAdd") : trans("home.projectEmpty")} diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx index 32e2258ed..ceef8b54a 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeResCard.tsx @@ -168,7 +168,6 @@ export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => voi )} { - console.log(res.isMarketplace); if (appNameEditing) { return; } diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx index a9525298b..2baac3259 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx @@ -345,7 +345,7 @@ export default function ApplicationHome() { selected ? : , }, { - text: {trans("home.modules")}, + text: {trans("home.allModules")}, routePath: MODULE_APPLICATIONS_URL, routeComp: ModuleView, icon: ({ selected, ...otherProps }) => @@ -356,6 +356,19 @@ export default function ApplicationHome() { ), visible: ({ user }) => user.orgDev, }, + { + text: {trans("home.marketplace")}, + routePath: MARKETPLACE_URL, + routePathExact: false, + routeComp: MarketplaceView, + icon: ({ selected, ...otherProps }) => + selected ? ( + + ) : ( + + ), + visible: ({ user }) => user.orgDev, + }, { text: {trans("home.trash")}, routePath: TRASH_URL, @@ -415,19 +428,6 @@ export default function ApplicationHome() { visible: ({ user }) => user.orgDev, onSelected: (_, currentPath) => currentPath.split("/")[1] === "datasource", }, - { - text: {trans("home.marketplace")}, - routePath: MARKETPLACE_URL, - routePathExact: false, - routeComp: MarketplaceView, - icon: ({ selected, ...otherProps }) => - selected ? ( - - ) : ( - - ), - visible: ({ user }) => user.orgDev, - }, { text: {trans("settings.title")}, routePath: SETTING, diff --git a/client/yarn.lock b/client/yarn.lock index 548e5b874..706f6c885 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -2058,6 +2058,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/aix-ppc64@npm:0.19.12" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm64@npm:0.18.20" @@ -2065,6 +2072,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm64@npm:0.19.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm@npm:0.18.20" @@ -2072,6 +2086,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm@npm:0.19.12" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-x64@npm:0.18.20" @@ -2079,6 +2100,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-x64@npm:0.19.12" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-arm64@npm:0.18.20" @@ -2086,6 +2114,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-arm64@npm:0.19.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-x64@npm:0.18.20" @@ -2093,6 +2128,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-x64@npm:0.19.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-arm64@npm:0.18.20" @@ -2100,6 +2142,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-arm64@npm:0.19.12" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-x64@npm:0.18.20" @@ -2107,6 +2156,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-x64@npm:0.19.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm64@npm:0.18.20" @@ -2114,6 +2170,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm64@npm:0.19.12" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm@npm:0.18.20" @@ -2121,6 +2184,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm@npm:0.19.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ia32@npm:0.18.20" @@ -2128,6 +2198,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ia32@npm:0.19.12" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-loong64@npm:0.18.20" @@ -2135,6 +2212,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-loong64@npm:0.19.12" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-mips64el@npm:0.18.20" @@ -2142,6 +2226,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-mips64el@npm:0.19.12" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ppc64@npm:0.18.20" @@ -2149,6 +2240,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ppc64@npm:0.19.12" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-riscv64@npm:0.18.20" @@ -2156,6 +2254,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-riscv64@npm:0.19.12" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-s390x@npm:0.18.20" @@ -2163,6 +2268,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-s390x@npm:0.19.12" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-x64@npm:0.18.20" @@ -2170,6 +2282,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-x64@npm:0.19.12" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/netbsd-x64@npm:0.18.20" @@ -2177,6 +2296,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/netbsd-x64@npm:0.19.12" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/openbsd-x64@npm:0.18.20" @@ -2184,6 +2310,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/openbsd-x64@npm:0.19.12" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/sunos-x64@npm:0.18.20" @@ -2191,6 +2324,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/sunos-x64@npm:0.19.12" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-arm64@npm:0.18.20" @@ -2198,6 +2338,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-arm64@npm:0.19.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-ia32@npm:0.18.20" @@ -2205,6 +2352,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-ia32@npm:0.19.12" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-x64@npm:0.18.20" @@ -2212,6 +2366,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-x64@npm:0.19.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -3523,6 +3684,97 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.12.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-android-arm64@npm:4.12.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.12.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.12.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.12.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.12.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.12.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.12.0" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.12.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.12.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.12.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.12.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.12.0": + version: 4.12.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.12.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rushstack/eslint-patch@npm:^1.1.0": version: 1.6.1 resolution: "@rushstack/eslint-patch@npm:1.6.1" @@ -3878,7 +4130,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0": +"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": version: 1.0.5 resolution: "@types/estree@npm:1.0.5" checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a @@ -7865,6 +8117,86 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.19.3": + version: 0.19.12 + resolution: "esbuild@npm:0.19.12" + dependencies: + "@esbuild/aix-ppc64": 0.19.12 + "@esbuild/android-arm": 0.19.12 + "@esbuild/android-arm64": 0.19.12 + "@esbuild/android-x64": 0.19.12 + "@esbuild/darwin-arm64": 0.19.12 + "@esbuild/darwin-x64": 0.19.12 + "@esbuild/freebsd-arm64": 0.19.12 + "@esbuild/freebsd-x64": 0.19.12 + "@esbuild/linux-arm": 0.19.12 + "@esbuild/linux-arm64": 0.19.12 + "@esbuild/linux-ia32": 0.19.12 + "@esbuild/linux-loong64": 0.19.12 + "@esbuild/linux-mips64el": 0.19.12 + "@esbuild/linux-ppc64": 0.19.12 + "@esbuild/linux-riscv64": 0.19.12 + "@esbuild/linux-s390x": 0.19.12 + "@esbuild/linux-x64": 0.19.12 + "@esbuild/netbsd-x64": 0.19.12 + "@esbuild/openbsd-x64": 0.19.12 + "@esbuild/sunos-x64": 0.19.12 + "@esbuild/win32-arm64": 0.19.12 + "@esbuild/win32-ia32": 0.19.12 + "@esbuild/win32-x64": 0.19.12 + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 2936e29107b43e65a775b78b7bc66ddd7d76febd73840ac7e825fb22b65029422ff51038a08d19b05154f543584bd3afe7d1ef1c63900429475b17fbe61cb61f + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -8794,7 +9126,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": +"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -8804,7 +9136,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": +"fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin, fsevents@patch:fsevents@~2.3.3#~builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#~builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -11505,7 +11837,7 @@ __metadata: react: ^18.2.0 react-dom: ^18.2.0 typescript: 4.8.4 - vite: ^4.5.2 + vite: ^5.0.12 vite-tsconfig-paths: ^3.6.0 languageName: unknown linkType: soft @@ -13563,6 +13895,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.35": + version: 8.4.35 + resolution: "postcss@npm:8.4.35" + dependencies: + nanoid: ^3.3.7 + picocolors: ^1.0.0 + source-map-js: ^1.0.2 + checksum: cf3c3124d3912a507603f6d9a49b3783f741075e9aa73eb592a6dd9194f9edab9d20a8875d16d137d4f779fe7b6fbd1f5727e39bfd1c3003724980ee4995e1da + languageName: node + linkType: hard + "preact@npm:~10.12.1": version: 10.12.1 resolution: "preact@npm:10.12.1" @@ -15650,6 +15993,60 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.2.0": + version: 4.12.0 + resolution: "rollup@npm:4.12.0" + dependencies: + "@rollup/rollup-android-arm-eabi": 4.12.0 + "@rollup/rollup-android-arm64": 4.12.0 + "@rollup/rollup-darwin-arm64": 4.12.0 + "@rollup/rollup-darwin-x64": 4.12.0 + "@rollup/rollup-linux-arm-gnueabihf": 4.12.0 + "@rollup/rollup-linux-arm64-gnu": 4.12.0 + "@rollup/rollup-linux-arm64-musl": 4.12.0 + "@rollup/rollup-linux-riscv64-gnu": 4.12.0 + "@rollup/rollup-linux-x64-gnu": 4.12.0 + "@rollup/rollup-linux-x64-musl": 4.12.0 + "@rollup/rollup-win32-arm64-msvc": 4.12.0 + "@rollup/rollup-win32-ia32-msvc": 4.12.0 + "@rollup/rollup-win32-x64-msvc": 4.12.0 + "@types/estree": 1.0.5 + fsevents: ~2.3.2 + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: a7398f072cf50804e9bdaf363792d0b7801800640434e7867c10b4e2e7be421ca2dc614ae0fc7392044eaf77d5c3a66f76a6fa2246bef97a7bc55926a8d60982 + languageName: node + linkType: hard + "rtl-css-js@npm:^1.16.1": version: 1.16.1 resolution: "rtl-css-js@npm:1.16.1" @@ -17699,6 +18096,46 @@ __metadata: languageName: node linkType: hard +"vite@npm:^5.0.12": + version: 5.1.4 + resolution: "vite@npm:5.1.4" + dependencies: + esbuild: ^0.19.3 + fsevents: ~2.3.3 + postcss: ^8.4.35 + rollup: ^4.2.0 + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: fb8b944c69fd738b412ad10471f01db01ed59b5d7fdf182b836b420b221a8bd5ada74d225a87aaa80cf8d2b693cc4a89ab7c291254a8b4e6faefd93843ebb9d3 + languageName: node + linkType: hard + "vlq@npm:^0.2.2": version: 0.2.3 resolution: "vlq@npm:0.2.3" From f42e8e7c54c08388dae412b8f51a2d9ae8878c3c Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Thu, 22 Feb 2024 20:00:15 +0100 Subject: [PATCH 10/10] Changes for AntIcon, Timeline Component --- client/VERSION | 2 +- client/package.json | 1 + .../lowcoder-design/src/components/edit.tsx | 2 +- .../src/icons}/antIcon.tsx | 21 +++++++ client/packages/lowcoder-design/src/index.ts | 2 + .../comps/comps/timelineComp/timelineComp.tsx | 59 +++++++++++-------- .../src/pages/common/headerStartDropdown.tsx | 2 +- client/yarn.lock | 10 ++++ 8 files changed, 70 insertions(+), 29 deletions(-) rename client/packages/{lowcoder/src/comps/comps/timelineComp => lowcoder-design/src/icons}/antIcon.tsx (98%) diff --git a/client/VERSION b/client/VERSION index cc6612c36..a6254504e 100644 --- a/client/VERSION +++ b/client/VERSION @@ -1 +1 @@ -2.3.0 \ No newline at end of file +2.3.1 \ No newline at end of file diff --git a/client/package.json b/client/package.json index fccf0fff1..f2054b530 100644 --- a/client/package.json +++ b/client/package.json @@ -70,6 +70,7 @@ }, "dependencies": { "@lottiefiles/react-lottie-player": "^3.5.3", + "@remixicon/react": "^4.1.1", "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.1", "@types/styled-components": "^5.1.34", diff --git a/client/packages/lowcoder-design/src/components/edit.tsx b/client/packages/lowcoder-design/src/components/edit.tsx index c093fa6a2..0a17bfe8a 100644 --- a/client/packages/lowcoder-design/src/components/edit.tsx +++ b/client/packages/lowcoder-design/src/components/edit.tsx @@ -68,7 +68,7 @@ const TextInput = styled(Input)` border: none; padding: 0 8px 0 4px; padding-left: ${(props) => (props.$hasPrefix ? "28px" : "4px")}; - color: #ffffff; + color: #444444; line-height: 28px; font-size: 14px; diff --git a/client/packages/lowcoder/src/comps/comps/timelineComp/antIcon.tsx b/client/packages/lowcoder-design/src/icons/antIcon.tsx similarity index 98% rename from client/packages/lowcoder/src/comps/comps/timelineComp/antIcon.tsx rename to client/packages/lowcoder-design/src/icons/antIcon.tsx index 7879ff920..34cd128f2 100644 --- a/client/packages/lowcoder/src/comps/comps/timelineComp/antIcon.tsx +++ b/client/packages/lowcoder-design/src/icons/antIcon.tsx @@ -1581,3 +1581,24 @@ export const ANTDICON = { zoominoutlined: , zoomoutoutlined: , }; + + +// Function to dynamically import icons +export const loadAntDIcon = async (iconName: string) => { + if (!iconName) return null; + + try { + const module = await import(`@ant-design/icons`); + const IconComponent = (module as any)[iconName]; + if (IconComponent) { + // Return the icon component if found + return ; + } else { + console.error(`Icon ${iconName} not found in @ant-design/icons`); + return null; + } + } catch (error) { + console.error(`Error loading icon ${iconName}:`, error); + return null; + } +}; \ No newline at end of file diff --git a/client/packages/lowcoder-design/src/index.ts b/client/packages/lowcoder-design/src/index.ts index a2a7ca495..92a05fb78 100644 --- a/client/packages/lowcoder-design/src/index.ts +++ b/client/packages/lowcoder-design/src/index.ts @@ -49,3 +49,5 @@ export * from "./components/toolTip"; export * from "./components/video"; export * from "./icons"; + +export * from "./icons/antIcon"; diff --git a/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx b/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx index 78a2787af..7433cc64d 100644 --- a/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx @@ -1,26 +1,19 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useContext } from "react"; import { default as Button } from "antd/es/button"; -// 渲染组件到编辑器 import { changeChildAction, DispatchType, CompAction, RecordConstructorToView, } from "lowcoder-core"; -// 文字国际化转换api import { trans } from "i18n"; -// 右侧属性栏总框架 import { UICompBuilder, withDefault } from "../../generators"; -// 右侧属性子框架 import { Section, sectionNames } from "lowcoder-design"; -// 指示组件是否隐藏的开关 import { hiddenPropertyView } from "comps/utils/propertyUtils"; -// 右侧属性开关 - import { BoolControl } from "comps/controls/boolControl"; -import { stringExposingStateControl } from "comps/controls/codeStateControl"; //文本并暴露值 +import { stringExposingStateControl } from "comps/controls/codeStateControl"; import { dropdownControl } from "comps/controls/dropdownControl"; -import { styleControl } from "comps/controls/styleControl"; //样式输入框 +import { styleControl } from "comps/controls/styleControl"; import { alignControl } from "comps/controls/alignControl"; import { AutoHeightControl } from "comps/controls/autoHeightControl"; import { jsonValueExposingStateControl } from "comps/controls/codeStateControl"; @@ -34,35 +27,25 @@ import { NumberControl, StringControl, } from "comps/controls/codeControl"; -// 事件控制 import { clickEvent, eventHandlerControl, } from "comps/controls/eventHandlerControl"; - -// 引入样式 import { TimeLineStyle, heightCalculator, widthCalculator, marginCalculator, } from "comps/controls/styleControlConstants"; -// 初始化暴露值 import { stateComp, valueComp } from "comps/generators/simpleGenerators"; -// 组件对外暴露属性的api import { NameConfig, NameConfigHidden, withExposingConfigs, } from "comps/generators/withExposing"; - import { timelineDate, timelineNode, TimelineDataTooltip } from "./timelineConstants"; import { convertTimeLineData } from "./timelineUtils"; import { default as Timeline } from "antd/es/timeline"; - -import { ANTDICON } from "./antIcon"; // todo: select icons to not import all icons - -import { useContext } from "react"; import { EditorContext } from "comps/editorState"; const EventOptions = [ @@ -86,18 +69,44 @@ const childrenMap = { clickedIndex: valueComp(0), }; +// Utility function to dynamically load Ant Design icons +const loadIcon = async (iconName: string) => { + if (!iconName) return null; + try { + const module = await import(`@ant-design/icons`); + const IconComponent = (module as any)[iconName]; + return IconComponent ? : null; + } catch (error) { + console.error(`Error loading icon ${iconName}:`, error); + return null; + } +}; + const TimelineComp = ( props: RecordConstructorToView & { dispatch: (action: CompAction) => void; } ) => { const { value, dispatch, style, mode, reverse, onEvent } = props; + const [icons, setIcons] = useState([]); + + useEffect(() => { + const loadIcons = async () => { + const iconComponents = await Promise.all( + value.map((node) => + node.dot ? loadIcon(node.dot) : Promise.resolve(null) + ) + ); + setIcons(iconComponents); + }; + + loadIcons(); + }, [value]); + const timelineItems = value.map((value: timelineNode, index: number) => ({ key: index, color: value?.color, - dot: value?.dot && ANTDICON.hasOwnProperty(value?.dot.toLowerCase()) - ? ANTDICON[value?.dot.toLowerCase() as keyof typeof ANTDICON] - : "", + dot: icons[index] || "", label: ( {value?.label} @@ -125,10 +134,8 @@ const TimelineComp = (

    ) - } - )) + })); - // TODO:parse px string return (
    void }) { )} > {isModule && ( diff --git a/client/yarn.lock b/client/yarn.lock index 706f6c885..95b2481e6 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -3387,6 +3387,15 @@ __metadata: languageName: node linkType: hard +"@remixicon/react@npm:^4.1.1": + version: 4.1.1 + resolution: "@remixicon/react@npm:4.1.1" + peerDependencies: + react: ">=18.2.0" + checksum: 59b05b91ad9335ae531b2927ceb7c5930ec5afcdabb5d96461d3181e1f794ddbdd4a899e85eb66f1a5341f55132898b6f680f51643bd7bd7244a741f44beb0b7 + languageName: node + linkType: hard + "@rjsf/antd@npm:^5.15.1": version: 5.15.1 resolution: "@rjsf/antd@npm:5.15.1" @@ -11921,6 +11930,7 @@ __metadata: "@babel/preset-env": ^7.20.2 "@babel/preset-typescript": ^7.18.6 "@lottiefiles/react-lottie-player": ^3.5.3 + "@remixicon/react": ^4.1.1 "@rollup/plugin-typescript": ^8.5.0 "@testing-library/jest-dom": ^5.16.5 "@testing-library/react": ^14.1.2