diff --git a/system-apps/app-store/pluginDefinition.json b/system-apps/app-store/pluginDefinition.json
index d439db1f..ebc3b1d3 100644
--- a/system-apps/app-store/pluginDefinition.json
+++ b/system-apps/app-store/pluginDefinition.json
@@ -3,7 +3,6 @@
"apiVersion": "1.0.0",
"pluginVersion": "1.0.0",
"pluginType": "application",
- "isSystemPlugin":true,
"webContent": {
"framework": "react",
"launchDefinition": {
diff --git a/system-apps/app-store/webClient/src/assets/download.svg b/system-apps/app-store/webClient/src/assets/download.svg
index c34f2db3..f766ac73 100644
--- a/system-apps/app-store/webClient/src/assets/download.svg
+++ b/system-apps/app-store/webClient/src/assets/download.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/system-apps/app-store/webClient/src/assets/installed.svg b/system-apps/app-store/webClient/src/assets/installed.svg
new file mode 100644
index 00000000..9778f413
--- /dev/null
+++ b/system-apps/app-store/webClient/src/assets/installed.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/system-apps/app-store/webClient/src/assets/license.svg b/system-apps/app-store/webClient/src/assets/license.svg
new file mode 100644
index 00000000..ebda24ff
--- /dev/null
+++ b/system-apps/app-store/webClient/src/assets/license.svg
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/system-apps/app-store/webClient/src/assets/uninstall.svg b/system-apps/app-store/webClient/src/assets/uninstall.svg
new file mode 100644
index 00000000..7cfe4658
--- /dev/null
+++ b/system-apps/app-store/webClient/src/assets/uninstall.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/system-apps/app-store/webClient/src/components/AppStore/LeftPanel/NavigationButtons.tsx b/system-apps/app-store/webClient/src/components/AppStore/LeftPanel/NavigationButtons.tsx
index 750552dc..8d87c192 100644
--- a/system-apps/app-store/webClient/src/components/AppStore/LeftPanel/NavigationButtons.tsx
+++ b/system-apps/app-store/webClient/src/components/AppStore/LeftPanel/NavigationButtons.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { Link } from "react-router-dom";
+import { Link, useLocation } from "react-router-dom";
const options = [
{
@@ -35,6 +35,7 @@ const options = [
];
const NavigationButtons = () => {
+ const location = useLocation();
return (
{
}}
>
{options.map((opt) => (
-
+
{opt.name}
))}
diff --git a/system-apps/app-store/webClient/src/components/AppStore/RightPanel/RightPanel.tsx b/system-apps/app-store/webClient/src/components/AppStore/RightPanel/RightPanel.tsx
index 2389df1d..81313e2b 100644
--- a/system-apps/app-store/webClient/src/components/AppStore/RightPanel/RightPanel.tsx
+++ b/system-apps/app-store/webClient/src/components/AppStore/RightPanel/RightPanel.tsx
@@ -10,6 +10,7 @@ const RightPanel: React.FC = ({
overflowY: "scroll",
paddingBottom: "40px",
minHeight: "100vh",
+ borderLeft: "1px solid #333",
}}>
{children}
diff --git a/system-apps/app-store/webClient/src/components/UI/AppCard.tsx b/system-apps/app-store/webClient/src/components/UI/AppCard.tsx
index ebaf17ed..0a1ebcf9 100644
--- a/system-apps/app-store/webClient/src/components/UI/AppCard.tsx
+++ b/system-apps/app-store/webClient/src/components/UI/AppCard.tsx
@@ -1,36 +1,52 @@
-import React from 'react';
-import { trunc } from '../../utils';
-import { ASSETS_URL } from '../../constants';
-import { Link } from 'react-router-dom';
+import React from "react";
+import { trunc } from "../../utils";
+import { ASSETS_URL } from "../../constants";
+import { Link } from "react-router-dom";
type AppProps = {
- name: string;
- icon: string;
- description: string;
- publisher: string;
- id: string;
+ name: string;
+ icon: string;
+ description: string;
+ publisher: string;
+ id: string;
+ installed: boolean;
};
-const App: React.FC = ({ name, icon, description, publisher, id }) => {
- return (
-
-
-
-
-
- {publisher}
-
- {name}
-
{trunc(description, 36)}
-
-
- Install
-
-
-
- );
+const App: React.FC = ({
+ name,
+ icon,
+ description,
+ publisher,
+ id,
+ installed,
+}) => {
+ return (
+
+
+
+
+ {publisher}
+ {name}
+
+
{trunc(description, 36)}
+
+
+ {installed ? "Installed" : "Install"}
+
+
+
+ );
};
export default App;
diff --git a/system-apps/app-store/webClient/src/components/UI/AppCarouselList.tsx b/system-apps/app-store/webClient/src/components/UI/AppCarouselList.tsx
index 5b00f927..c5b6797f 100644
--- a/system-apps/app-store/webClient/src/components/UI/AppCarouselList.tsx
+++ b/system-apps/app-store/webClient/src/components/UI/AppCarouselList.tsx
@@ -49,12 +49,13 @@ const AppCarouselList: React.FC = ({ apps }) => {
{apps.map((app) => (
))}
diff --git a/system-apps/app-store/webClient/src/context/apps.tsx b/system-apps/app-store/webClient/src/context/apps.tsx
new file mode 100644
index 00000000..9d167763
--- /dev/null
+++ b/system-apps/app-store/webClient/src/context/apps.tsx
@@ -0,0 +1,58 @@
+import React, { createContext, useContext, useState, useEffect } from "react";
+
+type AppsContextType = {
+ apps: any[];
+};
+
+const AppsContext = createContext(null);
+
+export const useAppsCtx = () => useContext(AppsContext);
+
+const AppsProvider: React.FC = ({ children }) => {
+ const [apps, setApps] = useState([]);
+ const [installedApps, setInstalledApps] = useState([]);
+
+ // fetch default apps
+ useEffect(() => {
+ const fetchApps = async () => {
+ // TODO: using mock server, need to use nodeserver
+ const allAppsResponse = await fetch(
+ "http://localhost:8000/appstore/api/apps"
+ );
+ const data = await allAppsResponse.json();
+ const installedAppsResponse = await fetch("/plugins?type=all");
+ const { pluginDefinitions } = await installedAppsResponse.json();
+ const serializedApps = data.map((app) => {
+ const installedApp = pluginDefinitions.find(
+ (installedApp) => installedApp.identifier === app.identifier
+ );
+ if (installedApp) {
+ return {
+ ...app,
+ installed: true,
+ };
+ }
+ return {
+ ...app,
+ installed: false,
+ };
+ });
+ setInstalledApps(serializedApps.filter((app) => app.installed));
+ setApps(serializedApps);
+ };
+ fetchApps();
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default AppsProvider;
diff --git a/system-apps/app-store/webClient/src/index.tsx b/system-apps/app-store/webClient/src/index.tsx
index 74da6bc9..c7276988 100644
--- a/system-apps/app-store/webClient/src/index.tsx
+++ b/system-apps/app-store/webClient/src/index.tsx
@@ -5,6 +5,7 @@ import { MVDResources } from "./context/mvd-resources";
import WindowSizeProvider from "./context/window-size";
import { RouterProvider } from "react-router-dom";
import { router } from "./routes";
+import AppsProvider from "./context/apps";
export function renderPlugin(
domElement: HTMLElement,
@@ -13,7 +14,9 @@ export function renderPlugin(
ReactDOM.render(
-
+
+
+
,
domElement
diff --git a/system-apps/app-store/webClient/src/pages/AppInfo.tsx b/system-apps/app-store/webClient/src/pages/AppInfo.tsx
index c03e9498..ca3101ff 100644
--- a/system-apps/app-store/webClient/src/pages/AppInfo.tsx
+++ b/system-apps/app-store/webClient/src/pages/AppInfo.tsx
@@ -1,18 +1,19 @@
import React from "react";
import AppStoreLayout from "../components/AppStore/AppStoreLayout";
import { useParams, Link } from "react-router-dom";
-import { APPS, ASSETS_URL } from "../constants";
-import { CustomPrevArrow } from "../components/UI";
+import { ASSETS_URL } from "../constants";
+import { useAppsCtx } from "../context/apps";
const AppPage = () => {
const { id } = useParams();
+ const { apps } = useAppsCtx();
if (!id) {
// TODO: return a not found page
return null;
}
- const app = APPS.find((app) => app.id === parseInt(id));
+ const app = apps?.find((app) => app.identifier === id);
return (
@@ -64,7 +65,7 @@ const AppPage = () => {
}}
>
{
marginBottom: "2px",
}}
>
- {app?.publisher}
+ {app?.author || "Zowe"}
{
marginBottom: "10px",
}}
>
- {app?.name}
+ {app.webContent.launchDefinition.pluginShortNameDefault}
{
marginBottom: "4px",
}}
>
- com.zowe.org
+ {app.identifier}
{
marginBottom: "4px",
}}
>
- v0.0.1
+ v{app.pluginVersion}
-
- Install
-
+
+
+ {app.installed ? "Installed" : "Install"}
+
+ {app.installed && (
+
+
+ Uninstall
+
+ )}
+
+
+
+ App Description: {app.webContent.descriptionDefault}
+
+ {app.license && (
+
+
+
+ License: {app.license}
+
+
+ )}
+ {app.homepage && (
+
+
+
+
+
+ )}
+
);
};
diff --git a/system-apps/app-store/webClient/src/pages/Discover.tsx b/system-apps/app-store/webClient/src/pages/Discover.tsx
index 56767ffc..d9c38caf 100644
--- a/system-apps/app-store/webClient/src/pages/Discover.tsx
+++ b/system-apps/app-store/webClient/src/pages/Discover.tsx
@@ -1,41 +1,48 @@
import React from "react";
import { Section, AppCarouselList } from "../components/UI";
-import { APPS } from "../constants";
-import AppStoreLayout from '../components/AppStore/AppStoreLayout';
+import AppStoreLayout from "../components/AppStore/AppStoreLayout";
+import { useAppsCtx } from "../context/apps";
-const sections = [
+const Discover = () => {
+ const { apps } = useAppsCtx();
+
+ if (!apps) {
+ // TODO: a nice loading spinner
+ return Loading...
;
+ }
+
+ const sections = [
{
- id: 1,
- title: "Popular Apps",
- apps: APPS
+ id: 1,
+ title: "Popular Apps",
+ apps: apps,
},
{
- id: 2,
- title: "Recently Updated",
- apps: APPS
+ id: 2,
+ title: "Recently Updated",
+ apps: apps,
},
{
- id: 3,
- title: "New Releases",
- apps: APPS
+ id: 3,
+ title: "New Releases",
+ apps: apps,
},
{
- id: 4,
- title: "Most Downloaded",
- apps: APPS
- }
-]
+ id: 4,
+ title: "Most Downloaded",
+ apps: apps,
+ },
+ ];
-const Discover = () => {
- return (
-
- {sections.map((section) => (
-
- ))}
-
- )
-}
+ return (
+
+ {sections.map((section) => (
+
+ ))}
+
+ );
+};
export default Discover;
diff --git a/system-apps/app-store/webClient/src/pages/Help.tsx b/system-apps/app-store/webClient/src/pages/Help.tsx
new file mode 100644
index 00000000..39a6471e
--- /dev/null
+++ b/system-apps/app-store/webClient/src/pages/Help.tsx
@@ -0,0 +1,87 @@
+import React from "react";
+import AppStoreLayout from "../components/AppStore/AppStoreLayout";
+import { Section } from "../components/UI";
+
+const Help = () => {
+ return (
+
+
+
+
+ The App Store uses zwe commands under the hood and requires a zowe
+ instance to be configured. If you have not configured it yet, please
+ follow the instructions below.
+
+
+ Note: Make changes to zowe.yaml file so that it understands
+ that you'll be installing plugins from external sources
+ (registries). Follow{" "}
+
+ this
+
+ .
+
+
+ For more help, please join our Slack channel or raise an issue on
+ GitHub.
+
+
+
+
+
+ );
+};
+
+export default Help;
diff --git a/system-apps/app-store/webClient/src/pages/InstalledApps.tsx b/system-apps/app-store/webClient/src/pages/InstalledApps.tsx
new file mode 100644
index 00000000..383bed97
--- /dev/null
+++ b/system-apps/app-store/webClient/src/pages/InstalledApps.tsx
@@ -0,0 +1,43 @@
+import React from "react";
+import AppStoreLayout from "../components/AppStore/AppStoreLayout";
+import { useAppsCtx } from "../context/apps";
+import { AppCard, Section } from "../components/UI";
+import { useWindowSize } from "../context/window-size";
+
+const InstalledApps = () => {
+ const { installedApps } = useAppsCtx();
+ const windowSize = useWindowSize();
+ return (
+
+
+
+ {installedApps.map((app) => (
+
+ ))}
+
+
+
+ );
+};
+
+export default InstalledApps;
diff --git a/system-apps/app-store/webClient/src/pages/OnPremise.tsx b/system-apps/app-store/webClient/src/pages/OnPremise.tsx
new file mode 100644
index 00000000..5fa3680b
--- /dev/null
+++ b/system-apps/app-store/webClient/src/pages/OnPremise.tsx
@@ -0,0 +1,45 @@
+import React from "react";
+import AppStoreLayout from "../components/AppStore/AppStoreLayout";
+import { Section } from "../components/UI";
+
+const OnPremise = () => {
+ return (
+
+
+
+
Enter your registry URL here
+
+
+ Save
+
+
+
+
+ );
+};
+
+export default OnPremise;
diff --git a/system-apps/app-store/webClient/src/pages/Settings.tsx b/system-apps/app-store/webClient/src/pages/Settings.tsx
new file mode 100644
index 00000000..6110c56a
--- /dev/null
+++ b/system-apps/app-store/webClient/src/pages/Settings.tsx
@@ -0,0 +1,55 @@
+import React from "react";
+import AppStoreLayout from "../components/AppStore/AppStoreLayout";
+import { Section } from "../components/UI";
+
+const Settings = () => {
+ return (
+
+
+
+
Enter your zowe config file path
+
+
+
Select a handler
+
+
+ npm
+ conda
+
+
+
+
+ Save
+
+
+
+
+ );
+};
+
+export default Settings;
diff --git a/system-apps/app-store/webClient/src/pages/Updates.tsx b/system-apps/app-store/webClient/src/pages/Updates.tsx
new file mode 100644
index 00000000..d72a4c59
--- /dev/null
+++ b/system-apps/app-store/webClient/src/pages/Updates.tsx
@@ -0,0 +1,8 @@
+import React from "react";
+import AppStoreLayout from "../components/AppStore/AppStoreLayout";
+
+const Updates = () => {
+ return Updates ;
+};
+
+export default Updates;
diff --git a/system-apps/app-store/webClient/src/routes.tsx b/system-apps/app-store/webClient/src/routes.tsx
index 8387784c..711fe0c9 100644
--- a/system-apps/app-store/webClient/src/routes.tsx
+++ b/system-apps/app-store/webClient/src/routes.tsx
@@ -1,6 +1,11 @@
import React from "react";
import { createMemoryRouter } from "react-router-dom";
import Discover from "./pages/Discover";
+import InstalledApps from "./pages/InstalledApps";
+import Help from "./pages/Help";
+import OnPremise from "./pages/OnPremise";
+import Settings from "./pages/Settings";
+import Updates from "./pages/Updates";
const AppInfo = React.lazy(() => import("./pages/AppInfo"));
@@ -25,6 +30,26 @@ export const router = createMemoryRouter(
),
},
+ {
+ path: "/installed",
+ element: ,
+ },
+ {
+ path: "/help",
+ element: ,
+ },
+ {
+ path: "/on-premise",
+ element: ,
+ },
+ {
+ path: "/settings",
+ element: ,
+ },
+ {
+ path: "/updates",
+ element: ,
+ },
],
{
initialEntries: ["/"],
diff --git a/system-apps/app-store/webClient/src/styles/globals.css b/system-apps/app-store/webClient/src/styles/globals.css
index ddb4bde1..8aea0ade 100644
--- a/system-apps/app-store/webClient/src/styles/globals.css
+++ b/system-apps/app-store/webClient/src/styles/globals.css
@@ -1,3 +1,12 @@
+a {
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: none;
+ cursor: pointer;
+}
+
.appStoreContainer {
font-family: "Roboto", sans-serif;
background-color: #2f2f2f;
@@ -92,6 +101,11 @@
justify-content: center;
}
+.installButton[disabled] {
+ background: #555;
+ cursor: not-allowed;
+}
+
.appList {
width: 90%;
}
@@ -115,6 +129,11 @@
font-weight: 500;
}
+.optionButton.active {
+ background: #343434;
+ color: #fff;
+}
+
.optionButton:focus {
outline: none;
}
@@ -124,6 +143,45 @@
color: #fff;
}
+select {
+ appearance: none;
+ outline: 0;
+ border: 0;
+ box-shadow: none;
+ flex: 1;
+ padding: 0 1em;
+ color: #fff;
+ background-color: #333;
+ background-image: none;
+ cursor: pointer;
+}
+select::-ms-expand {
+ display: none;
+}
+.select {
+ position: relative;
+ display: flex;
+ width: 20em;
+ height: 3em;
+ border-radius: 0.25em;
+ overflow: hidden;
+}
+/* Arrow */
+.select::after {
+ content: "\25BC";
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 1em;
+ background-color: #333;
+ transition: 0.25s all ease;
+ pointer-events: none;
+}
+/* Transition */
+.select:hover::after {
+ color: #f39c12;
+}
+
::-webkit-scrollbar {
width: 6px;
}
diff --git a/system-apps/app-store/webClient/src/utils/index.js b/system-apps/app-store/webClient/src/utils/index.js
index 19143895..2c72d866 100644
--- a/system-apps/app-store/webClient/src/utils/index.js
+++ b/system-apps/app-store/webClient/src/utils/index.js
@@ -1 +1 @@
-export const trunc = (str, n) => str.length > n ? str.substring(0, n - 1) + '...' : str;
+export const trunc = (str, n) => str && str.length > n ? str.substring(0, n - 1) + '...' : str;