Many nice things
This commit is contained in:
17
src/api/common.ts
Normal file
17
src/api/common.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import axios from "axios";
|
||||
|
||||
export class ApiBase {}
|
||||
|
||||
const commonHeaders = {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Max-Age": 60,
|
||||
};
|
||||
|
||||
export const axiosInstance = axios.create({
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
...commonHeaders,
|
||||
},
|
||||
});
|
||||
12
src/api/dashboard.ts
Normal file
12
src/api/dashboard.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { axiosInstance, ApiBase } from "./common";
|
||||
|
||||
class DashboardApi extends ApiBase {
|
||||
get = async (endpoint: string) => {
|
||||
return axiosInstance.get(endpoint, {});
|
||||
};
|
||||
getFake = async (endpoint: string) => {
|
||||
return axiosInstance.get("http://localhost:8002", {});
|
||||
};
|
||||
}
|
||||
|
||||
export const dashboardApi = new DashboardApi();
|
||||
1
src/api/index.ts
Normal file
1
src/api/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { dashboardApi } from "./dashboard";
|
||||
@ -1,11 +1,60 @@
|
||||
import React from "react";
|
||||
import EnvTabsContainer from "./EnvTabsContainer";
|
||||
import { EnvTab } from "../types";
|
||||
import LoadingIndicator from "./LoadingIndicator";
|
||||
import { DashboardLoadError, DashboardResponseContent, EnvTab, Environment } from "../types";
|
||||
import { ENVIRONMENT_TABS } from "../const";
|
||||
import { dashboardApi } from "../api";
|
||||
import { AxiosError } from "axios";
|
||||
import LoadingError from "./LoadingError";
|
||||
import EnvironmentList from "./dashboard/EnvironmentList";
|
||||
|
||||
export default function Dashboard() {
|
||||
const onSelectEnvTab = (tab: EnvTab): void => {
|
||||
console.log("tab changed:", tab);
|
||||
const defaultEnv = ENVIRONMENT_TABS[0];
|
||||
|
||||
const [selectedEnv, setSelectedEnv] = React.useState(defaultEnv);
|
||||
const [loadingEnv, setLoadingEnv] = React.useState(false);
|
||||
const [loadingErr, setLoadingErr] = React.useState<DashboardLoadError | null>(null);
|
||||
const [activeEnvironments, setActiveEnvironments] = React.useState<Environment[] | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
loadEnv(defaultEnv);
|
||||
}, []);
|
||||
|
||||
const onSelectEnvTab = (envTab: EnvTab): void => {
|
||||
loadEnv(envTab);
|
||||
};
|
||||
|
||||
return <EnvTabsContainer onSelect={onSelectEnvTab} />;
|
||||
const loadEnv = (envTab: EnvTab) => {
|
||||
console.log("loading env:", envTab);
|
||||
setLoadingErr(null);
|
||||
setLoadingEnv(true);
|
||||
|
||||
dashboardApi
|
||||
.getFake(envTab.dashboardEndpoint)
|
||||
.then((response) => {
|
||||
return response.data.content;
|
||||
})
|
||||
.then((data: DashboardResponseContent) => {
|
||||
setActiveEnvironments(data.environments);
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
console.error(error);
|
||||
setLoadingErr({
|
||||
message: error.message,
|
||||
url: envTab.dashboardEndpoint,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setLoadingEnv(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<LoadingIndicator active={loadingEnv} />
|
||||
<EnvTabsContainer selectedEnv={selectedEnv} onSelect={onSelectEnvTab} />
|
||||
{loadingErr && <LoadingError error={loadingErr} />}
|
||||
{activeEnvironments && <EnvironmentList environments={activeEnvironments} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,13 +10,12 @@ interface OnSelectTab {
|
||||
}
|
||||
|
||||
interface EnvTabsContainerProps {
|
||||
selectedEnv: EnvTab;
|
||||
onSelect: OnSelectTab;
|
||||
}
|
||||
|
||||
export default function EnvTabsContainer({ onSelect }: EnvTabsContainerProps) {
|
||||
const defaultTab = ENVIRONMENT_TABS[0];
|
||||
|
||||
const [selected, setSelected] = React.useState(defaultTab);
|
||||
export default function EnvTabsContainer({ selectedEnv, onSelect }: EnvTabsContainerProps) {
|
||||
const [selected, setSelected] = React.useState(selectedEnv);
|
||||
|
||||
const handleChange = (event: React.SyntheticEvent, newValue: EnvTab) => {
|
||||
setSelected(newValue);
|
||||
@ -28,7 +27,7 @@ export default function EnvTabsContainer({ onSelect }: EnvTabsContainerProps) {
|
||||
});
|
||||
|
||||
return (
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<Box sx={{ width: "100%", marginBottom: "2rem" }}>
|
||||
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
|
||||
<Tabs value={selected} onChange={handleChange}>
|
||||
{tabs}
|
||||
|
||||
17
src/components/LoadingError.tsx
Normal file
17
src/components/LoadingError.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from "react";
|
||||
import { DashboardLoadError } from "../types";
|
||||
import Alert from "@mui/material/Alert";
|
||||
import AlertTitle from "@mui/material/AlertTitle";
|
||||
|
||||
interface LoadingErrorProps {
|
||||
error: DashboardLoadError;
|
||||
}
|
||||
|
||||
export default function LoadingError({ error }: LoadingErrorProps) {
|
||||
return (
|
||||
<Alert severity="error">
|
||||
<AlertTitle>{error.message}</AlertTitle>
|
||||
<p>URL: {error.url}</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
15
src/components/LoadingIndicator.tsx
Normal file
15
src/components/LoadingIndicator.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import LinearProgress from "@mui/material/LinearProgress";
|
||||
|
||||
interface LoadingIndicatorProps {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export default function LoadingIndicator({ active }: LoadingIndicatorProps) {
|
||||
return (
|
||||
<Box sx={{ width: "100%" }}>
|
||||
{active ? <LinearProgress /> : <Box className="linear-progress-placeholder" />}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
24
src/components/dashboard/EnvironmentList.tsx
Normal file
24
src/components/dashboard/EnvironmentList.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import { Environment } from "../../types";
|
||||
import TenantList from "./TenantList";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
interface EnvironmentListProps {
|
||||
environments: Environment[];
|
||||
}
|
||||
|
||||
export default function EnvironmentList({ environments }: EnvironmentListProps) {
|
||||
const envItems = environments.map((env) => {
|
||||
return (
|
||||
<Box className="environment">
|
||||
<Typography component={"h1"} key={env.name} className="title">
|
||||
{env.name}
|
||||
</Typography>
|
||||
<TenantList tenants={env.tenants} />
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
return <Box className="environment-list">{envItems}</Box>;
|
||||
}
|
||||
22
src/components/dashboard/ServiceList.tsx
Normal file
22
src/components/dashboard/ServiceList.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import { Service } from "../../types";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface ServiceListProps {
|
||||
services: Service[];
|
||||
}
|
||||
|
||||
export default function ServiceList({ services }: ServiceListProps) {
|
||||
const serviceItems = services.map((service) => {
|
||||
return (
|
||||
<Box className="service-card">
|
||||
<Typography component={"h1"} key={service.name} className="service-name">
|
||||
{service.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
return <Box className="service-list">{serviceItems}</Box>;
|
||||
}
|
||||
25
src/components/dashboard/TenantList.tsx
Normal file
25
src/components/dashboard/TenantList.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React from "react";
|
||||
import { Tenant } from "../../types";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import ServiceList from "./ServiceList";
|
||||
|
||||
interface TenantListProps {
|
||||
tenants: Tenant[];
|
||||
}
|
||||
|
||||
export default function TenantList({ tenants: tenants }: TenantListProps) {
|
||||
const tenantItems = tenants.map((tenant) => {
|
||||
const tenantName = tenant.name !== "default" ? `Tenant: ${tenant.name}` : "Multitenant";
|
||||
return (
|
||||
<Box className="tenant">
|
||||
<Typography component={"h1"} key={tenant.name} className="tenant-name">
|
||||
{tenantName}
|
||||
</Typography>
|
||||
<ServiceList services={tenant.services} />
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
return <Box className="tenant-list">{tenantItems}</Box>;
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
color: whitesmoke;
|
||||
padding: 1rem 0 1rem 0;
|
||||
box-shadow: 0px 5px 11px 0px rgba(0, 0, 0, 0.31);
|
||||
margin-bottom: 1rem;
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
letter-spacing: 5px;
|
||||
@ -12,13 +11,13 @@
|
||||
color: #08ab08;
|
||||
animation: rotation 3s infinite linear;
|
||||
@keyframes rotation {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
src/scss/_linear-progress.scss
Normal file
3
src/scss/_linear-progress.scss
Normal file
@ -0,0 +1,3 @@
|
||||
.linear-progress-placeholder {
|
||||
height: 4px;
|
||||
}
|
||||
@ -1,2 +1,3 @@
|
||||
@import "common";
|
||||
@import "app-header";
|
||||
@import "linear-progress";
|
||||
|
||||
@ -3,3 +3,49 @@ export type EnvTab = {
|
||||
id: string;
|
||||
dashboardEndpoint: string;
|
||||
};
|
||||
|
||||
type StatusPerTenant = { [tenantId: string]: boolean };
|
||||
|
||||
type HealthCheckStatus = {
|
||||
status_ok: boolean;
|
||||
message: string;
|
||||
status_per_tenant: StatusPerTenant | null;
|
||||
};
|
||||
|
||||
type AppDetails = {
|
||||
app_name: string;
|
||||
app_version: string;
|
||||
template_name: string | null;
|
||||
template_version: string | null;
|
||||
};
|
||||
|
||||
type Node = {
|
||||
name: string;
|
||||
url: string;
|
||||
app_details: AppDetails | null;
|
||||
health_check_status: HealthCheckStatus;
|
||||
};
|
||||
|
||||
export type Service = {
|
||||
name: string;
|
||||
nodes: Node[];
|
||||
};
|
||||
|
||||
export type Tenant = {
|
||||
name: string;
|
||||
services: Service[];
|
||||
};
|
||||
|
||||
export type Environment = {
|
||||
name: string;
|
||||
tenants: Tenant[];
|
||||
};
|
||||
|
||||
export type DashboardResponseContent = {
|
||||
environments: Environment[];
|
||||
};
|
||||
|
||||
export type DashboardLoadError = {
|
||||
url: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user