Skip to content

Apps Marketplace #699

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 6 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions client/packages/lowcoder-design/src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
22 changes: 21 additions & 1 deletion client/packages/lowcoder/src/api/applicationApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
Expand All @@ -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<HomeDataResponse> {
return Api.get(ApplicationApi.fetchHomeDataURL, request);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
9 changes: 9 additions & 0 deletions client/packages/lowcoder/src/api/orgApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
Expand All @@ -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<GenericApiResponse<OrgGroup>> {
return Api.post(OrgApi.createGroupURL, request);
Expand Down Expand Up @@ -127,6 +132,10 @@ export class OrgApi extends Api {
static updateOrg(request: UpdateOrgPayload): AxiosPromise<ApiResponse> {
return Api.put(OrgApi.updateOrgURL(request.id), request);
}

static fetchAPIUsage(orgId: string, lastMonthOnly?: boolean): AxiosPromise<ApiResponse> {
return Api.get(OrgApi.fetchUsage(orgId), lastMonthOnly);
}
}

export default OrgApi;
2 changes: 2 additions & 0 deletions client/packages/lowcoder/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -138,6 +139,7 @@ class AppIndex extends React.Component<AppIndexProps, any> {
FOLDER_URL,
TRASH_URL,
SETTING,
MARKETPLACE_URL,
]}
// component={ApplicationListPage}
component={ApplicationHome}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div style={{ marginBottom: "22px" }}>
<PermissionSwitchWrapper>
<TacoSwitch
checked={isPublicToMarketplace}
onChange={(checked) => {
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'}
/>
</PermissionSwitchWrapper>
<PermissionSwitchWrapper>
<TacoSwitch
checked={isPublic}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ const SegmentedControlBasicComp = (function () {
return new UICompBuilder(SegmentChildrenMap, (props) => {
const [
validateState,
handleValidate,
handleChange,
] = useSelectInputValidate(props);
return props.label({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export const useSelectInputValidate = (props: ValidationParams) => {

return [
validateState,
handleValidate,
// handleValidate,
handleChange,
] as const;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const AppUILayoutType: Record<AppTypeEnum, UiLayoutType> = {
[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";

Expand All @@ -36,6 +36,7 @@ export interface ApplicationMeta {
containerSize?: { height: number; width: number };
createBy: string;
createAt: number;
creatorEmail?: string;
orgId: string;
role: ApplicationRoleType;
extra: ApplicationExtra;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -135,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",
Expand Down
1 change: 1 addition & 0 deletions client/packages/lowcoder/src/constants/routesURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
Expand Down
1 change: 1 addition & 0 deletions client/packages/lowcoder/src/i18n/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2053,6 +2053,7 @@ export const de = {
"modules": "Module",
"module": "Modul",
"trash": "Papierkorb",
"marketplace": "Marktplatz",
"queryLibrary": "Abfragebibliothek",
"datasource": "Datenquellen",
"selectDatasourceType": "Datenquellentyp auswählen",
Expand Down
1 change: 1 addition & 0 deletions client/packages/lowcoder/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions client/packages/lowcoder/src/i18n/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2120,6 +2120,7 @@ home: {
modules: "模块",
module: "模块",
trash: "回收站",
marketplace: "市场",
queryLibrary: "查询管理",
datasource: "数据源",
selectDatasourceType: "选择数据源类型",
Expand Down
18 changes: 11 additions & 7 deletions client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -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',
}
);

Expand Down Expand Up @@ -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 && (
<CreateDropdown defaultVisible={showNewUserGuide(user)} mode={mode} />
)}
</OperationRightWrapper>
Expand Down Expand Up @@ -421,11 +423,13 @@ export function HomeLayout(props: HomeLayoutProps) {
<div style={{ marginBottom: "16px" }}>
{mode === "trash"
? trans("home.trashEmpty")
: mode === "marketplace"
? "No apps in marketplace yet"
: user.orgDev
? trans("home.projectEmptyCanAdd")
: trans("home.projectEmpty")}
</div>
{mode !== "trash" && user.orgDev && <CreateDropdown mode={mode} />}
{mode !== "trash" && mode !== "marketplace" && user.orgDev && <CreateDropdown mode={mode} />}
</EmptyView>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
handleAppEditClick,
handleAppViewClick,
handleFolderViewClick,
handleMarketplaceAppViewClick,
HomeResInfo,
} from "../../util/homeResUtils";
import { HomeResOptions } from "./HomeResOptions";
Expand Down Expand Up @@ -167,6 +168,7 @@ export function HomeResCard(props: { res: HomeRes; onMove: (res: HomeRes) => voi
)}
<CardInfo
onClick={(e) => {
console.log(res.isMarketplace);
if (appNameEditing) {
return;
}
Expand All @@ -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);
}
}}
Expand Down Expand Up @@ -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)
}
>
Expand Down
Loading