Skip to content
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
2,716 changes: 2,578 additions & 138 deletions package-lock.json

Large diffs are not rendered by default.

68 changes: 40 additions & 28 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
import AppRouter from "./AppRouter";
import { useAuth } from "react-oidc-context";
import LoginView from "./views/Login";
import "@ui5/webcomponents-icons/dist/AllIcons.d.ts";
import { useEffect, useState } from "react";
import { SessionExpiringDialog } from "./components/Dialogs/SessionExpiringDialog.tsx";
import { useFrontendConfig } from "./context/FrontendConfigContext.tsx";
import { useTranslation } from "react-i18next";
import AppRouter from './AppRouter';
import { useAuth } from 'react-oidc-context';
import LoginView from './views/Login';
import '@ui5/webcomponents-icons/dist/AllIcons.d.ts';
import { useEffect, useState } from 'react';
import { SessionExpiringDialog } from './components/Dialogs/SessionExpiringDialog.tsx';
import { useFrontendConfig } from './context/FrontendConfigContext.tsx';
import { useTranslation } from 'react-i18next';

function App() {
const auth = useAuth();
const [dialogSessionExpiringIsOpen, setDialogSessionExpiringIsOpen] = useState(false);
const [dialogSessionExpiringIsOpen, setDialogSessionExpiringIsOpen] =
useState(false);
const { t } = useTranslation();

const [hasActiveSession, setHasActiveSession] = useState(auth.isAuthenticated);
const [hasActiveSession, setHasActiveSession] = useState(
auth.isAuthenticated,
);

useEffect(() => {
setHasActiveSession(auth.isAuthenticated)
}, [auth.isAuthenticated])
setHasActiveSession(auth.isAuthenticated);
}, [auth.isAuthenticated]);

useEffect(() => {
const unregisterAccessTokenExpiring = auth.events.addAccessTokenExpiring(() => {
setDialogSessionExpiringIsOpen(true)
})
const unregisterAccessTokenExpired = auth.events.addAccessTokenExpired(() => {
console.error('access token expired, show login view')
setDialogSessionExpiringIsOpen(false)
setHasActiveSession(false)
})
const unregisterAccessTokenExpiring = auth.events.addAccessTokenExpiring(
() => {
setDialogSessionExpiringIsOpen(true);
},
);
const unregisterAccessTokenExpired = auth.events.addAccessTokenExpired(
() => {
console.error('access token expired, show login view');
setDialogSessionExpiringIsOpen(false);
setHasActiveSession(false);
},
);

return () => {
unregisterAccessTokenExpiring()
unregisterAccessTokenExpired()
}
}, [auth.events])
unregisterAccessTokenExpiring();
unregisterAccessTokenExpired();
};
}, [auth.events]);

const frontendConfig = useFrontendConfig();

Expand All @@ -47,14 +54,19 @@ function App() {
}

if (!hasActiveSession) {
return <>
<LoginView />
</>
return (
<>
<LoginView />
</>
);
}

return (
<>
<SessionExpiringDialog isOpen={dialogSessionExpiringIsOpen} setIsOpen={setDialogSessionExpiringIsOpen} />
<SessionExpiringDialog
isOpen={dialogSessionExpiringIsOpen}
setIsOpen={setDialogSessionExpiringIsOpen}
/>
<AppRouter />
</>
);
Expand Down
28 changes: 16 additions & 12 deletions src/AppRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { HashRouter as Router, Navigate, Route, Routes } from "react-router-dom";
import ControlPlaneView from "./views/ControlPlanes/ControlPlaneView.tsx";
import ProjectListView from "./views/ProjectList";
import ControlPlaneListView from "./views/ControlPlanes/ControlPlaneListView.tsx";
import GlobalProviderOutlet from "./components/Core/ApiConfigWrapper.tsx";
import { ShellBarComponent } from "./components/Core/ShellBar.tsx";
import {
HashRouter as Router,
Navigate,
Route,
Routes,
} from 'react-router-dom';
import ControlPlaneView from './views/ControlPlanes/ControlPlaneView.tsx';
import ProjectListView from './views/ProjectList';
import ControlPlaneListView from './views/ControlPlanes/ControlPlaneListView.tsx';
import GlobalProviderOutlet from './components/Core/ApiConfigWrapper.tsx';
import { ShellBarComponent } from './components/Core/ShellBar.tsx';

function AppRouter() {
return (
Expand All @@ -13,18 +18,17 @@ function AppRouter() {
<Routes>
<Route path="/mcp" element={<GlobalProviderOutlet />}>
<Route path="projects" element={<ProjectListView />} />
<Route path="projects/:projectName" element={<ControlPlaneListView />} />
<Route
path="projects/:projectName"
element={<ControlPlaneListView />}
/>
<Route
path="projects/:projectName/workspaces/:workspaceName/mcps/:controlPlaneName/context/:contextName"
element={<ControlPlaneView />}
/>
</Route>
<Route
path="/"
element={<Navigate to="/mcp/projects" />}
/>
<Route path="/" element={<Navigate to="/mcp/projects" />} />
</Routes>

</Router>
</>
);
Expand Down
37 changes: 17 additions & 20 deletions src/components/ControlPlane/ComponentList.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,44 @@
import {

AnalyticalTable,
AnalyticalTableColumnDefinition
} from "@ui5/webcomponents-react";
import { ControlPlaneType } from "../../lib/api/types/crate/controlPlanes";
AnalyticalTableColumnDefinition,
} from '@ui5/webcomponents-react';
import { ControlPlaneType } from '../../lib/api/types/crate/controlPlanes';
import { useTranslation } from 'react-i18next';

export default function ComponentList({ mcp }: { mcp: ControlPlaneType }) {

const { t } = useTranslation();

const data = [
{
name: "Crossplane",
version: mcp.spec?.components.crossplane?.version
name: 'Crossplane',
version: mcp.spec?.components.crossplane?.version,
},
{
name: "BTP Service Operator",
version: mcp.spec?.components.btpServiceOperator?.version
name: 'BTP Service Operator',
version: mcp.spec?.components.btpServiceOperator?.version,
},
{
name: "External Secrets Operator",
version: mcp.spec?.components.externalSecretsOperator?.version
name: 'External Secrets Operator',
version: mcp.spec?.components.externalSecretsOperator?.version,
},
{
name: "Kyverno",
version: mcp.spec?.components.kyverno?.version
name: 'Kyverno',
version: mcp.spec?.components.kyverno?.version,
},
{
name: "Flux",
version: mcp.spec?.components.flux?.version
}
name: 'Flux',
version: mcp.spec?.components.flux?.version,
},
].filter((item) => item.version !== undefined);

const componentTableColumns: AnalyticalTableColumnDefinition[] = [
{
Header: t('ComponentList.tableComponentHeader'),
accessor: "name",
accessor: 'name',
},
{
Header: t('ComponentList.tableVersionHeader'),
accessor: "version",
accessor: 'version',
},
];

Expand All @@ -52,7 +50,6 @@ export default function ComponentList({ mcp }: { mcp: ControlPlaneType }) {
minRows={0}
data={data}
/>

</div>
)
);
}
35 changes: 23 additions & 12 deletions src/components/ControlPlane/FluxList.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import ConfiguredAnalyticstable from "../Shared/ConfiguredAnalyticsTable.tsx";
import { AnalyticalTableColumnDefinition, Title } from "@ui5/webcomponents-react";
import ReactTimeAgo from "react-time-ago";
import IllustratedError from "../Shared/IllustratedError.tsx";
import useResource from "../../lib/api/useApiResource";
import { FluxGitRepo } from "../../lib/api/types/flux/listGitRepo";
import { FluxKustomization } from "../../lib/api/types/flux/listKustomization";
import ConfiguredAnalyticstable from '../Shared/ConfiguredAnalyticsTable.tsx';
import {
AnalyticalTableColumnDefinition,
Title,
} from '@ui5/webcomponents-react';
import ReactTimeAgo from 'react-time-ago';
import IllustratedError from '../Shared/IllustratedError.tsx';
import useResource from '../../lib/api/useApiResource';
import { FluxGitRepo } from '../../lib/api/types/flux/listGitRepo';
import { FluxKustomization } from '../../lib/api/types/flux/listKustomization';
import { useTranslation } from 'react-i18next';

export default function FluxList() {
const { data: repoData, error: repoErr, isLoading: repoIsLoading } = useResource(FluxGitRepo); //404 if component not enabled
const { data: kustmizationData, error: kustomizationErr, isLoading: kustomizationIsLoading } = useResource(FluxKustomization); //404 if component not enabled
const {
data: repoData,
error: repoErr,
isLoading: repoIsLoading,
} = useResource(FluxGitRepo); //404 if component not enabled
const {
data: kustmizationData,
error: kustomizationErr,
isLoading: kustomizationIsLoading,
} = useResource(FluxKustomization); //404 if component not enabled

const { t } = useTranslation();

Expand All @@ -23,15 +34,15 @@ export default function FluxList() {
const columns: AnalyticalTableColumnDefinition[] = [
{
Header: t('FluxList.tableNameHeader'),
accessor: "metadata.name",
accessor: 'metadata.name',
},
{
Header: t('FluxList.tableStatusHeader'),
accessor: "status.usages",
accessor: 'status.usages',
},
{
Header: t('FluxList.tableCreatedHeader'),
accessor: "metadata.creationTimestamp",
accessor: 'metadata.creationTimestamp',
Cell: (props: any) => <ReactTimeAgo date={new Date(props.cell.value)} />,
},
];
Expand Down
69 changes: 42 additions & 27 deletions src/components/ControlPlane/MCPHealthPopoverButton.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { AnalyticalTable, Icon, Popover } from "@ui5/webcomponents-react";
import { AnalyticalTableColumnDefinition } from "@ui5/webcomponents-react/wrappers";
import PopoverPlacement from "@ui5/webcomponents/dist/types/PopoverPlacement.js";
import "@ui5/webcomponents-icons/dist/copy";
import { JSX, useRef, useState } from "react";
import { ControlPlaneStatusType, ReadyStatus } from "../../lib/api/types/crate/controlPlanes";
import ReactTimeAgo from "react-time-ago";
import { AnimatedHoverTextButton } from "../Helper/AnimatedHoverTextButton.tsx";
import { AnalyticalTable, Icon, Popover } from '@ui5/webcomponents-react';
import { AnalyticalTableColumnDefinition } from '@ui5/webcomponents-react/wrappers';
import PopoverPlacement from '@ui5/webcomponents/dist/types/PopoverPlacement.js';
import '@ui5/webcomponents-icons/dist/copy';
import { JSX, useRef, useState } from 'react';
import {
ControlPlaneStatusType,
ReadyStatus,
} from '../../lib/api/types/crate/controlPlanes';
import ReactTimeAgo from 'react-time-ago';
import { AnimatedHoverTextButton } from '../Helper/AnimatedHoverTextButton.tsx';
import { useTranslation } from 'react-i18next';

export default function MCPHealthPopoverButton({ mcpStatus }: { mcpStatus: ControlPlaneStatusType | undefined }) {
export default function MCPHealthPopoverButton({
mcpStatus,
}: {
mcpStatus: ControlPlaneStatusType | undefined;
}) {
const popoverRef = useRef(null);
const [open, setOpen] = useState(false);

const { t } = useTranslation();

const handleOpenerClick = (e: any) => {
Expand All @@ -25,33 +32,33 @@ export default function MCPHealthPopoverButton({ mcpStatus }: { mcpStatus: Contr
const statusTableColumns: AnalyticalTableColumnDefinition[] = [
{
Header: t('MCPHealthPopoverButton.statusHeader'),
accessor: "status",
accessor: 'status',
width: 50,
Cell: (instance: any) => {
const isReady = instance.cell.value === "True";
const isReady = instance.cell.value === 'True';
return (
<Icon
style={{ color: isReady ? "green" : "red" }}
name={isReady ? "sap-icon://sys-enter" : "sap-icon://pending"}
></Icon>
style={{ color: isReady ? 'green' : 'red' }}
name={isReady ? 'sap-icon://sys-enter' : 'sap-icon://pending'}
/>
);
},
},
{
Header: t('MCPHealthPopoverButton.typeHeader'),
accessor: "type",
accessor: 'type',
},
{
Header: t('MCPHealthPopoverButton.messageHeader'),
accessor: "message",
accessor: 'message',
},
{
Header: t('MCPHealthPopoverButton.reasonHeader'),
accessor: "reason",
accessor: 'reason',
},
{
Header: t('MCPHealthPopoverButton.transitionHeader'),
accessor: "lastTransitionTime",
accessor: 'lastTransitionTime',
Cell: (instance: any) => {
return <ReactTimeAgo date={new Date(instance.cell.value)} />;
},
Expand All @@ -61,18 +68,24 @@ export default function MCPHealthPopoverButton({ mcpStatus }: { mcpStatus: Contr
return (
<div className="component-title-row">
<AnimatedHoverTextButton
onClick={handleOpenerClick}
icon={getIconForOverallStatus(mcpStatus?.status)}
text={mcpStatus?.status ?? ""}
text={mcpStatus?.status ?? ''}
onClick={handleOpenerClick}
/>
<Popover ref={popoverRef} open={open} placement={PopoverPlacement.Bottom}>
{<StatusTable status={mcpStatus} tableColumns={statusTableColumns}/>}
{<StatusTable status={mcpStatus} tableColumns={statusTableColumns} />}
</Popover>
</div>
);
}

function StatusTable({ status, tableColumns }: { status: ControlPlaneStatusType | undefined, tableColumns: AnalyticalTableColumnDefinition[]}) {
function StatusTable({
status,
tableColumns,
}: {
status: ControlPlaneStatusType | undefined;
tableColumns: AnalyticalTableColumnDefinition[];
}) {
return (
<div style={{ width: 600 }}>
<AnalyticalTable
Expand All @@ -88,14 +101,16 @@ function StatusTable({ status, tableColumns }: { status: ControlPlaneStatusType
);
}

export function getIconForOverallStatus(status: ReadyStatus | undefined): JSX.Element {
export function getIconForOverallStatus(
status: ReadyStatus | undefined,
): JSX.Element {
switch (status) {
case ReadyStatus.Ready:
return <Icon style={{ color: "green" }} name="sap-icon://sys-enter" />;
return <Icon style={{ color: 'green' }} name="sap-icon://sys-enter" />;
case ReadyStatus.NotReady:
return <Icon style={{ color: "red" }} name="sap-icon://pending" />;
return <Icon style={{ color: 'red' }} name="sap-icon://pending" />;
case ReadyStatus.InDeletion:
return <Icon style={{ color: "orange" }} name="sap-icon://delete" />;
return <Icon style={{ color: 'orange' }} name="sap-icon://delete" />;
case undefined:
return <></>;
}
Expand Down
Loading