From 5cfac16940958f1586ae58e6b00e34bf29b929a2 Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Sat, 24 Feb 2024 14:36:10 +0100 Subject: [PATCH] Adapting Marketplace --- .../src/constants/applicationConstants.ts | 40 +++++++++++ .../packages/lowcoder/src/i18n/locales/en.ts | 4 ++ .../packages/lowcoder/src/i18n/locales/zh.ts | 4 ++ .../src/pages/ApplicationV2/HomeLayout.tsx | 56 +++++++++++++-- .../pages/ApplicationV2/MarketplaceView.tsx | 55 +++++++------- .../src/pages/ApplicationV2/index.tsx | 21 +----- .../src/pages/common/headerStartDropdown.tsx | 72 +++++++++++-------- .../src/pages/common/previewHeader.tsx | 5 +- 8 files changed, 169 insertions(+), 88 deletions(-) diff --git a/client/packages/lowcoder/src/constants/applicationConstants.ts b/client/packages/lowcoder/src/constants/applicationConstants.ts index 90130e18b..339548ab0 100644 --- a/client/packages/lowcoder/src/constants/applicationConstants.ts +++ b/client/packages/lowcoder/src/constants/applicationConstants.ts @@ -13,6 +13,40 @@ export enum AppTypeEnum { MobileTabLayout = 6, } +export enum ApplicationCategoriesEnum { + BUSINESS = "Business", + DASHBOARD = "Dashboards & Reporting", + SLIDES = "Slides & Presentations", + WEBSITE = "Website", + SHOPPING = "Shopping & Ecommerce", + TOOLS = "Tools & Internal Apps", + COMMUNICATION = "Communication", + PRODUCTIVITY = "Productivity", + EDUCATION = "Education", + SOCIAL_MEDIA = "Social Media", + ENTERTAINMENT = "Entertainment", + FINANCE = "Finance", + HEALTH_FITNESS = "Health & Fitness", + LIFESTYLE = "Lifestyle", + NEWS_MAGAZINES = "News & Magazines", + PERSONALIZATION = "Personalization", + PHOTOGRAPHY = "Photography", + SPORTS = "Sports", + TRAVEL_LOCAL = "Travel & Local", + WEATHER = "Weather", + MEDICAL = "Medical", + MUSIC_AUDIO = "Music & Audio", + AUTO_VEHICLES = "Auto & Vehicles", + ART_DESIGN = "Art & Design", + BEAUTY = "Beauty", + DATING = "Dating", + EVENTS = "Events", + FOOD_DRINK = "Food & Drink", + HOUSE_HOME = "House & Home", + PARENTING = "Parenting", + MAPS_NAVIGATION = "Maps & Navigation", +}; + export const AppUILayoutType: Record = { [AppTypeEnum.Application]: "normal", [AppTypeEnum.Module]: "module", @@ -37,6 +71,11 @@ export interface ApplicationMeta { createBy: string; createAt: number; creatorEmail?: string; + title?: string; + description?: string; + icon?: string; + category?: ApplicationCategoriesEnum; + showheader?: boolean; orgId: string; role: ApplicationRoleType; extra: ApplicationExtra; @@ -44,6 +83,7 @@ export interface ApplicationMeta { lastViewTime: number; folderId: string; folder: false; + isLocalMarketplace?: boolean; applicationStatus: "NORMAL" | "RECYCLED" | "DELETED"; } diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index a40d69d2e..bbe46953f 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -2304,10 +2304,14 @@ export const en = { "copyLink": "Copy link", "appPublicMessage": "Make the app public. Anyone can view.", "modulePublicMessage": "Make the module public. Anyone can view.", + "marketplaceURL": "https://api-service.lowcoder.cloud", "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", + "errorMarketplaceApps": "Error while loading Marketplace Apps", + "localMarketplaceTitle": "Local Marketplace", + "globalMarketplaceTitle": "Lowcoder 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 b90d3a8f1..f39827bcb 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -2183,10 +2183,14 @@ home: { copyLink: "复制链接", appPublicMessage: "将应用设为公开,任何人都可以查看.", modulePublicMessage: "将模块设为公开,任何人都可以查看.", + "marketplaceURL": "https://api-service.lowcoder.cloud", "appMarketplaceMessage": "发布您的应用程序到Lowcoder市场.任何人都可以在那里查看和复制它.", "moduleMarketplaceMessage": "发布您的模块到Lowcoder市场.任何人都可以在那里查看和复制它.", "marketplaceGoodPublishing": "请确保您的应用程序命名准确、易于使用。发布前请删除任何敏感信息。此外,移除本地数据源,代之以静态内置临时数据", "noMarketplaceApps": "市场上还没有应用程序", + "errorMarketplaceApps": "获取市场应用程序错误", + "localMarketplaceTitle": "本地市场", + "globalMarketplaceTitle": "Lowcoder 市场", 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 9ea75b051..ff6b6cf27 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/HomeLayout.tsx @@ -33,6 +33,8 @@ import { trans } from "../../i18n"; import { isFetchingFolderElements } from "../../redux/selectors/folderSelector"; import { checkIsMobile } from "util/commonUtils"; import MarketplaceHeaderImage from "assets/images/marketplaceHeaderImage.jpg"; +import { Divider } from "antd"; +import { Margin } from "../setting/theme/styledComponents"; const Wrapper = styled.div` display: flex; @@ -257,6 +259,7 @@ export interface HomeRes { isManageable: boolean; isDeletable: boolean; isMarketplace?: boolean; + isLocalMarketplace?: boolean; } export type HomeBreadcrumbType = { text: string; path: string }; @@ -266,11 +269,13 @@ export type HomeLayoutMode = "view" | "trash" | "module" | "folder" | "folders" export interface HomeLayoutProps { breadcrumb?: HomeBreadcrumbType[]; elements: Array; + localMarketplaceApps?: Array; + globalMarketplaceApps?: Array; mode: HomeLayoutMode; } export function HomeLayout(props: HomeLayoutProps) { - const { breadcrumb = [], elements = [], mode } = props; + const { breadcrumb = [], elements = [], localMarketplaceApps = [], globalMarketplaceApps = [],mode } = props; const user = useSelector(getUser); const isFetching = useSelector(isFetchingFolderElements); @@ -288,7 +293,17 @@ export function HomeLayout(props: HomeLayoutProps) { return null; } - const resList: HomeRes[] = elements + var displayElements = elements; + if (mode === "marketplace") { + const markedLocalApps = localMarketplaceApps.map(app => ({ ...app, isLocalMarketplace: true })); + const markedGlobalApps = globalMarketplaceApps.map(app => ({ ...app, isLocalMarketplace: false })); + // Merge local and global apps into the elements array + displayElements = [...markedLocalApps, ...markedGlobalApps]; + } + + console.log("HomeLayout: displayElements", displayElements); + + const resList: HomeRes[] = displayElements .filter((e) => searchValue ? e.name.toLocaleLowerCase().includes(searchValue) || @@ -331,6 +346,7 @@ export function HomeLayout(props: HomeLayoutProps) { isManageable: mode !== 'marketplace' && canManageApp(user, e), isDeletable: mode !== 'marketplace' && canEditApp(user, e), isMarketplace: mode === 'marketplace', + isLocalMarketplace: e.isLocalMarketplace, } ); @@ -420,7 +436,6 @@ export function HomeLayout(props: HomeLayoutProps) { - {isFetching && resList.length === 0 ? ( ) : ( @@ -434,11 +449,38 @@ export function HomeLayout(props: HomeLayoutProps) { setLayout(layout === "list" ? "card" : "list")}> {layout === "list" ? : } - {layout === "list" ? ( - - ) : ( - + + {mode === "marketplace" && ( + <> + {layout === "list" ? ( + <> +

{trans("home.localMarketplaceTitle")}

+ app.isLocalMarketplace)} /> + +

{trans("home.globalMarketplaceTitle")}

+ !app.isLocalMarketplace)} /> + + ) : ( + <> +

{trans("home.localMarketplaceTitle")}

+ app.isLocalMarketplace)} /> + +

{trans("home.globalMarketplaceTitle")}

+ !app.isLocalMarketplace)} /> + + )} + )} + {mode !== "marketplace" && ( + <> + {layout === "list" ? ( + + ) : ( + + )} + + )} + )} diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx index f8091174b..55df189c3 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/MarketplaceView.tsx @@ -13,54 +13,49 @@ import log from "loglevel"; export function MarketplaceView() { const [ marketplaceApps, setMarketplaceApps ] = useState>([]); - const marketplaceType = matchPath<{marketplaceType?: MarketplaceType}>(window.location.pathname, MARKETPLACE_TYPE_URL)?.params - .marketplaceType; - const isLowcoderMarketplace = marketplaceType === 'lowcoder'; - const marketplaceBreadcrumbText = !marketplaceType?.length - ? trans("home.marketplace") - : marketplaceType === 'lowcoder' - ? `${trans("home.marketplace")} (Lowcoder)` - : `${trans("home.marketplace")} (Local)`; - - const fetchLowcoderMarketplaceApps = () => { - const http = axios.create({ - baseURL: 'https://api-service.lowcoder.cloud', - withCredentials: false, - }); - return http.get(`/api/v1/applications/marketplace-apps`); - }; - - const fetchLocalMarketplaceApps = () => { - return ApplicationApi.fetchAllMarketplaceApps() - } + const [ localMarketplaceApps, setLocalMarketplaceApps ] = useState>([]); const fetchMarketplaceApps = async () => { try { let response: AxiosResponse>; - if(isLowcoderMarketplace) { - response = await fetchLowcoderMarketplaceApps(); - } else { - response = await fetchLocalMarketplaceApps(); + const http = axios.create({ + baseURL: trans("home.marketplaceURL"), + withCredentials: false, + }); + response = await http.get(`/api/v1/applications/marketplace-apps`); + const isValidResponse: boolean = validateResponse(response); + if (isValidResponse) { + setMarketplaceApps(response.data.data); } + } catch (error: any) { + messageInstance.error(trans("home.errorMarketplaceApps")); + } + } + const fetchLocalMarketplaceApps = async () => { + try { + let response: AxiosResponse>; + response = await ApplicationApi.fetchAllMarketplaceApps(); const isValidResponse: boolean = validateResponse(response); if (isValidResponse) { - setMarketplaceApps(response.data.data); + setLocalMarketplaceApps(response.data.data); } } catch (error: any) { - messageInstance.error(error.message); - log.debug("fetch marketplace apps error: ", error); + messageInstance.error(trans("home.errorMarketplaceApps")); } } useEffect(() => { fetchMarketplaceApps(); - }, [marketplaceType]); + fetchLocalMarketplaceApps(); + }, []); return ( ); diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx index c2d3c435e..76ccf765d 100644 --- a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx +++ b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx @@ -363,14 +363,10 @@ export default function ApplicationHome() { { text: ( - { - isSelfHost - ? `${trans("home.marketplace")} (Local)` - : trans("home.marketplace") - } + {trans("home.marketplace")} ), - routePath: isSelfHost ? MARKETPLACE_URL_BY_TYPE('local') : MARKETPLACE_URL, + routePath: MARKETPLACE_URL, routePathExact: false, routeComp: MarketplaceView, icon: ({ selected, ...otherProps }) => @@ -381,19 +377,6 @@ export default function ApplicationHome() { ), visible: ({ user }) => user.orgDev, }, - { - text: {`${trans("home.marketplace")} (Lowcoder)`}, - routePath: MARKETPLACE_URL_BY_TYPE('lowcoder'), - routePathExact: false, - routeComp: MarketplaceView, - icon: ({ selected, ...otherProps }) => - selected ? ( - - ) : ( - - ), - visible: ({ user }) => user.orgDev && isSelfHost, - }, { text: {trans("home.trash")}, routePath: TRASH_URL, diff --git a/client/packages/lowcoder/src/pages/common/headerStartDropdown.tsx b/client/packages/lowcoder/src/pages/common/headerStartDropdown.tsx index 6fe44ec93..be16727fe 100644 --- a/client/packages/lowcoder/src/pages/common/headerStartDropdown.tsx +++ b/client/packages/lowcoder/src/pages/common/headerStartDropdown.tsx @@ -69,7 +69,7 @@ export const TypeName = { [AppTypeEnum.MobileTabLayout]: trans("home.mobileTabLayout"), }; -export function HeaderStartDropdown(props: { setEdit: () => void }) { +export function HeaderStartDropdown(props: { setEdit: () => void, isViewMarketplaceMode?: boolean}) { const user = useSelector(getUser); const showAppSnapshot = useSelector(showAppSnapshotSelector); const applicationId = useApplicationId(); @@ -80,35 +80,49 @@ export function HeaderStartDropdown(props: { setEdit: () => void }) { const isModule = appType === AppTypeEnum.Module; const isEditable = canEditApp(user, application); + const isMarketplace = props.isViewMarketplaceMode; + + const menuItems = useMemo(() => { + // Define a base array with items that are always visible + const items = [ + { + key: "export", + label: {trans("header.export")}, + visible: true, + }, + { + key: "duplicate", + label: ( + + {trans("header.duplicate", { + type: TypeName[application?.applicationType!]?.toLowerCase(), + })} + + ), + visible: true, + }, + ]; + + // Conditionally add items based on isEditable and isMarketplace flags + if (!isMarketplace) { + items.unshift( + { + key: "edit", + label: {trans("header.editName")}, + visible: isEditable, + }, + { + key: "delete", + label: {trans("home.moveToTrash")}, + visible: isEditable, + } + ); + } + + return items; + }, [isEditable, isMarketplace]); + - 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 ( <> diff --git a/client/packages/lowcoder/src/pages/common/previewHeader.tsx b/client/packages/lowcoder/src/pages/common/previewHeader.tsx index 85ae198d6..faaa28f53 100644 --- a/client/packages/lowcoder/src/pages/common/previewHeader.tsx +++ b/client/packages/lowcoder/src/pages/common/previewHeader.tsx @@ -144,9 +144,8 @@ export const PreviewHeader = () => { {isViewMarketplaceMode && ( { - - }} + setEdit={() => { }} + isViewMarketplaceMode={isViewMarketplaceMode} /> )} {!isViewMarketplaceMode && (