diff --git a/dashboard/client/.prettierrc.js b/dashboard/client/.prettierrc.js
index 1087cf3a3..13ed8bbea 100644
--- a/dashboard/client/.prettierrc.js
+++ b/dashboard/client/.prettierrc.js
@@ -1,3 +1,3 @@
module.exports = {
- ...require('../.prettierrc'),
+ ...require('../../.prettierrc'),
};
diff --git a/dashboard/client/config-compiler.js b/dashboard/client/config-compiler.js
index 4e1f3fa5b..a1e39b57b 100644
--- a/dashboard/client/config-compiler.js
+++ b/dashboard/client/config-compiler.js
@@ -1,6 +1,41 @@
(function (config, env) {
+ const whiteList = [
+ 'DATABOX_API_URL',
+ 'DATABOX_CLIENT_URL',
+ 'DEV_MODE',
+ 'DISPLAY_SERVICES_MENU',
+ 'DOCKER_TAG',
+ 'ELASTICHQ_URL',
+ 'EXPOSE_API_URL',
+ 'EXPOSE_CLIENT_URL',
+ 'KEYCLOAK_URL',
+ 'MAILHOG_URL',
+ 'MATOMO_URL',
+ 'NOTIFY_API_URL',
+ 'PGADMIN_URL',
+ 'PHPMYADMIN_URL',
+ 'RABBITMQ_CONSOLE_URL',
+ 'REPORT_API_URL',
+ 'SAML_URL',
+ 'SAML2_URL',
+ 'STACK_NAME',
+ 'TRAEFIK_CONSOLE_URL',
+ 'UPLOADER_API_URL',
+ 'UPLOADER_CLIENT_URL',
+ 'ZIPPY_URL',
+ ];
+
+ const e = {};
+
+ Object.entries(env).forEach(([key, value]) => {
+ if (whiteList.includes(key)) {
+ e[key] = value;
+ }
+ })
+
+
return {
locales: config.available_locales,
- env,
+ env: e,
};
});
diff --git a/dashboard/client/index.tpl.html b/dashboard/client/index.tpl.html
index bada4d06f..f28d40056 100644
--- a/dashboard/client/index.tpl.html
+++ b/dashboard/client/index.tpl.html
@@ -1,10 +1,10 @@
-
-
+
+
Phrasea Dashboard
__TPL_CONFIG__
-
-
-
-
-
-
-
+ You can add webfonts, meta tags, or analytics to this file.
+ The build step will place the bundled scripts into the tag.
+
+ To begin the development, run `npm start` or `yarn start`.
+ To create a production bundle, use `npm run build` or `yarn build`.
+-->
+
diff --git a/dashboard/client/package.json b/dashboard/client/package.json
index c02d642c6..80dde20b4 100644
--- a/dashboard/client/package.json
+++ b/dashboard/client/package.json
@@ -12,10 +12,12 @@
},
"dependencies": {
"@alchemy/theme-editor": "workspace:*",
+ "@mui/material": "^5.15.0",
+ "@mui/icons-material": "^5.15.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "vite-plugin-svgr": "^4.2.0",
- "@mui/material": "^5.15.0"
+ "react-google-font-loader": "^1.1.0",
+ "vite-plugin-svgr": "^4.2.0"
},
"devDependencies": {
"@types/node": "^18.8.5",
diff --git a/dashboard/client/src/ClientApp.tsx b/dashboard/client/src/ClientApp.tsx
new file mode 100644
index 000000000..0ce2f71ad
--- /dev/null
+++ b/dashboard/client/src/ClientApp.tsx
@@ -0,0 +1,32 @@
+import Service, {ServiceBaseProps} from "./Service.tsx";
+import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
+import ApiIcon from '@mui/icons-material/Api';
+
+type Props = {
+ apiUrl: string;
+ clientUrl: string;
+} & ServiceBaseProps;
+
+export default function ClientApp({
+ apiUrl,
+ clientUrl,
+ ...props
+}: Props) {
+
+ return ,
+ href: `${apiUrl}/admin`,
+ title: `Admin of ${props.title}`
+ },
+ {
+ icon: ,
+ href: apiUrl,
+ title: `API documentation of ${props.title}`
+ },
+ ]}
+ {...props}
+ />
+}
diff --git a/dashboard/client/src/Root.tsx b/dashboard/client/src/Root.tsx
index e75f2fa97..9a510be7f 100644
--- a/dashboard/client/src/Root.tsx
+++ b/dashboard/client/src/Root.tsx
@@ -1,6 +1,65 @@
+import {Container, Grid} from "@mui/material";
+import Service from "./Service";
+import ClientApp from "./ClientApp.tsx";
+import config from "./config.ts";
+
type Props = {};
export default function Root({}: Props) {
+ const {
+ DATABOX_API_URL,
+ EXPOSE_API_URL,
+ UPLOADER_API_URL,
+ NOTIFY_API_URL,
+ KEYCLOAK_URL,
+ DATABOX_CLIENT_URL,
+ EXPOSE_CLIENT_URL,
+ UPLOADER_CLIENT_URL,
+ } = config.env;
+
+ console.log('config.env', config.env);
- return <>Dashboard!>
+ return
+
+
+ {DATABOX_API_URL && }
+ {EXPOSE_API_URL && }
+ {UPLOADER_API_URL && }
+ {NOTIFY_API_URL && }
+
+
}
diff --git a/dashboard/client/src/Service.tsx b/dashboard/client/src/Service.tsx
new file mode 100644
index 000000000..8721df822
--- /dev/null
+++ b/dashboard/client/src/Service.tsx
@@ -0,0 +1,91 @@
+import {Button, Card, CardActions, CardContent, CardMedia, Grid, IconButton, Typography,} from '@mui/material';
+import {JSX, PropsWithChildren, ReactNode} from "react";
+
+type BaseProps = {
+ title: string;
+ mainUrl?: string;
+ description?: ReactNode;
+ logo?: string;
+};
+
+type AppLink = {
+ icon: ReactNode;
+ title: string;
+ href: string;
+}
+
+type Props = {
+ links?: AppLink[];
+} & BaseProps;
+
+export type {BaseProps as ServiceBaseProps};
+
+export default function Service({
+ title,
+ logo,
+ description,
+ mainUrl,
+ links = [],
+}: Props) {
+ return
+
+
+ ({
+ height: 140,
+ backgroundSize: 'contain',
+ backgroundColor: theme.palette.background.default,
+ })}
+ image={logo}
+ title="green iguana"
+ />
+
+
+
+
+ {title}
+
+
+ {description &&
+ {description}
+ }
+
+
+ {links.map(({href, icon, title}, i) =>
+ {icon}
+ )}
+
+
+
+}
+
+function Link({href, children}: PropsWithChildren<{
+ href: string | undefined;
+}>) {
+ if (href) {
+ return
+ {children}
+
+ }
+
+ return children as JSX.Element;
+}
diff --git a/dashboard/client/src/config.ts b/dashboard/client/src/config.ts
index de65dee48..8be4f8b82 100644
--- a/dashboard/client/src/config.ts
+++ b/dashboard/client/src/config.ts
@@ -3,7 +3,31 @@ declare global {
interface Window {
config: {
locales: string[];
- env: Record;
+ env: {
+ DATABOX_API_URL: string;
+ DATABOX_CLIENT_URL: string;
+ DEV_MODE: string;
+ DISPLAY_SERVICES_MENU: string;
+ DOCKER_TAG: string;
+ ELASTICHQ_URL: string;
+ EXPOSE_API_URL: string;
+ EXPOSE_CLIENT_URL: string;
+ KEYCLOAK_URL: string;
+ MAILHOG_URL: string;
+ MATOMO_URL: string;
+ NOTIFY_API_URL: string;
+ PGADMIN_URL: string;
+ PHPMYADMIN_URL: string;
+ RABBITMQ_CONSOLE_URL: string;
+ REPORT_API_URL: string;
+ SAML_URL: string;
+ SAML2_URL: string;
+ STACK_NAME: string;
+ TRAEFIK_CONSOLE_URL: string;
+ UPLOADER_API_URL: string;
+ UPLOADER_CLIENT_URL: string;
+ ZIPPY_URL: string;
+ };
};
}
}
diff --git a/dashboard/client/src/index.tsx b/dashboard/client/src/index.tsx
index e7cc266e3..512bd8058 100644
--- a/dashboard/client/src/index.tsx
+++ b/dashboard/client/src/index.tsx
@@ -1,9 +1,69 @@
import ReactDOM from 'react-dom/client';
import Root from './Root.tsx';
import React from 'react';
+import {CssBaseline, GlobalStyles, ThemeOptions} from "@mui/material";
+import {ThemeEditorProvider} from '@alchemy/theme-editor';
+
+const theme: ThemeOptions = {
+ typography: {
+ fontFamily: '\'Montserrat\', sans-serif',
+ h1: {
+ fontSize: 24,
+ fontWeight: 600,
+ },
+ h2: {
+ fontSize: 19,
+ fontWeight: 600,
+ },
+ h5: {
+ fontSize: 19,
+ },
+ },
+ palette: {
+ primary: {
+ main: '#003249',
+ contrastText: '#e7eaea',
+ },
+ secondary: {
+ main: '#007EA7',
+ },
+ common: {
+ white: '#e7eaea',
+ },
+ background: {
+ default: '#85dbff',
+ }
+ },
+}
+
+const scrollbarWidth = 3;
ReactDOM.createRoot(document.getElementById('root')!).render(
-
+
+
+ ({
+ '*': {
+ '*::-webkit-scrollbar': {
+ width: scrollbarWidth,
+ },
+ '*::-webkit-scrollbar-track': {
+ borderRadius: 10,
+ },
+ '*::-webkit-scrollbar-thumb': {
+ borderRadius: scrollbarWidth,
+ backgroundColor: theme.palette.primary.main,
+ },
+ },
+ 'body': {
+ backgroundColor: theme.palette.common.white,
+ },
+ })}
+ />
+
+
);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4363f8f76..f14656588 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,6 +20,9 @@ importers:
'@alchemy/theme-editor':
specifier: workspace:*
version: link:../../lib/js/theme-editor
+ '@mui/icons-material':
+ specifier: ^5.15.0
+ version: 5.15.0(@mui/material@5.15.0)(@types/react@18.2.39)(react@18.2.0)
'@mui/material':
specifier: ^5.15.0
version: 5.15.0(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
@@ -29,6 +32,9 @@ importers:
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
+ react-google-font-loader:
+ specifier: ^1.1.0
+ version: 1.1.0(react-dom@18.2.0)(react@18.2.0)
vite-plugin-svgr:
specifier: ^4.2.0
version: 4.2.0(typescript@5.3.2)(vite@5.0.7)
@@ -17666,6 +17672,17 @@ packages:
react: 18.2.0
dev: false
+ /react-google-font-loader@1.1.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-mnPYxBdhYEldTtREYPKllbmMvCGkTbpdmNjR6iTM3eTNWcg17OrfXjq9HRDZe+1+mhnUnI/AOcpvG0LIQPcLIw==}
+ peerDependencies:
+ react: ^16.6.3
+ react-dom: ^16.6.3
+ dependencies:
+ prop-types: 15.8.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/react-grid-gallery@0.5.6(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-x+JYmt8iHVzcoeji7hG33IQdzJ4LmcOpgg0zS/mAPf7FsGtSgo/UpfhUPWduhDBglMTG1xbbw/z/kiZb7J9Pcg==}
peerDependencies: