Skip to content

Commit

Permalink
[feat] partial conf route and display
Browse files Browse the repository at this point in the history
  • Loading branch information
Camille Moussu committed Nov 20, 2024
1 parent 9964bf6 commit 6f45a1e
Show file tree
Hide file tree
Showing 8 changed files with 761 additions and 177 deletions.
351 changes: 229 additions & 122 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "llng-manager",
"version": "0.1.0",
"private": true,
"proxy": "http://manager.example.com:19876",
"allowedHosts": "all",
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
Expand All @@ -21,6 +21,7 @@
"@types/react-router-dom": "^5.3.3",
"@types/react-router-redux": "^5.0.27",
"axios": "^1.7.4",
"http-proxy-middleware": "^3.0.3",
"i18next": "^23.10.1",
"i18next-browser-languagedetector": "^7.2.1",
"i18next-http-backend": "^2.5.0",
Expand All @@ -38,7 +39,7 @@
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"start": "HOST=newmanager.example.com react-scripts start",
"build": "react-scripts build",
"test": "jest",
"jest-preview": "jest-preview",
Expand Down
22 changes: 19 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useAppSelector } from "./app/hooks";
import { history } from "./app/store";
import Navbar from "./components/Navbar";
import { Configuration } from "./pages/Configuration";
import { PartialConfiguration } from "./pages/PartialConfiguration";

function App() {
useTranslation();
Expand All @@ -26,14 +27,29 @@ function App() {
type: infos ? infos[0] : "",
info: infos
? {
name: infos.length === 3 ? infos[2] : infos[1],
type: infos.length === 3 ? infos[1] : "",
}
name: infos.length === 3 ? infos[2] : infos[1],
type: infos.length === 3 ? infos[1] : "",
}
: { name: "", type: "" },
}}
/>
}
/>
<Route
path="manager.partial.html"
element={
<PartialConfiguration
location={{
type: infos ? infos[0] : "",
info: infos
? {
name: infos.length === 3 ? infos[2] : infos[1],
type: infos.length === 3 ? infos[1] : "",
}
: { name: "", type: "" },
}} />
}
/>
</Routes>
</Router>
</Suspense>
Expand Down
6 changes: 3 additions & 3 deletions src/components/SaveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { Fab } from "@mui/material";
import { t } from "i18next";
import { useState } from "react";
import { useAppDispatch, useAppSelector } from "../app/hooks";
import { saveConfigAsync } from "../features/config/configSlice";
import { saveConfigAsync, savePartialConfigAsync } from "../features/config/configSlice";
import { ruleOIDC, ruleSAML } from "../utils/rules";
import "./SaveButton.css";
import { SavePopup } from "./SavePopup";
export default function SaveButton() {
export default function SaveButton({ partial }: { partial?: boolean }) {
const [openSavePopup, setOpenSavePopup] = useState(false);
const [openErrorPopup, setOpenErrorPopup] = useState(false);
const dispatch = useAppDispatch();
Expand Down Expand Up @@ -46,7 +46,7 @@ export default function SaveButton() {
});
}
if (stateOk) {
dispatch(saveConfigAsync(config.data.config));
dispatch(partial ? savePartialConfigAsync(config.data.config) : saveConfigAsync(config.data.config));
setOpenSavePopup(true);
} else {
setOpenErrorPopup(true);
Expand Down
290 changes: 290 additions & 0 deletions src/dashboards/PartialManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
import CachedIcon from "@mui/icons-material/Cached";
import WidgetsOutlinedIcon from "@mui/icons-material/WidgetsOutlined";
import { Button, Divider, Menu, MenuItem, Pagination } from "@mui/material";
import { ChangeEvent, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useLocation } from "react-router-dom";
import { push } from "redux-first-history";
import { useAppDispatch, useAppSelector } from "../app/hooks";
import AppCard from "../components/managerComponents/AppCard";
import FilterToggle from "../components/managerComponents/Filters";
import Issuers from "../components/managerComponents/Issuers";
import {
getPartialConfigAsync,
removeError,
setError,
} from "../features/config/configSlice";
import { ruleCAS, ruleOIDC, ruleSAML } from "../utils/rules";
import "./Manager.css";

export default function PartialManager() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const config = useAppSelector((state) => state.config);
const configNum = useAppSelector((state) =>
state.router.location?.hash.replace("#conf/", "")
);
const [filters, setFilters] = useState({ alpha: false, search: "" });
const location = useLocation();
const [page, setPage] = useState(1);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const [configPresent, setConfigPresent] = useState<boolean>(
Boolean(config.data.metadata && !config.loading && !config.error.has)
);

useEffect(() => {
if (!configPresent) {
setConfigPresent(true);
dispatch(
getPartialConfigAsync(configNum === "latest" ? undefined : Number(configNum))
);
}
const appNum =
(config.data.config.locationRules ? Object.keys(config.data.config.locationRules).length : 0) +
(config.data.config.samlSPMetaDataXML
? Object.keys(config.data.config.samlSPMetaDataXML).length
: 0) +
(config.data.config.oidcRPMetaDataOptions
? Object.keys(config.data.config.oidcRPMetaDataOptions).length
: 0) +
(config.data.config.casAppMetaDataOptions
? Object.keys(config.data.config.casAppMetaDataOptions).length
: 0);
if (appNum === 1) {
const name = config.data.config.locationRules || config.data.config.samlSPMetaDataXML || config.data.config.oidcRPMetaDataOptions || config.data.config.casAppMetaDataOptions
const type = config.data.config.locationRules ? "native" : false || config.data.config.samlSPMetaDataXML ? "SPsaml" : false || config.data.config.oidcRPMetaDataOptions ? "RPoidc" : false || config.data.config.casAppMetaDataOptions ? "AppCas" : false
console.log(Object.keys(name ? name : {})[0])
dispatch(push(`#app/${type}/${Object.keys(name ? name : {})[0]}`))
}
}, [dispatch, configNum, config.data.metadata, location, configPresent]);
try {
if (config.loading) {
return (
<div>
<strong className="title"> {t("currentConfiguration")} </strong>
{t("loading")}
</div>
);
} else if (config.error.has) {
return (
<div>
<strong className="title"> {t("currentConfiguration")} </strong>
<strong>{t("failedLoading")}</strong>
<span>{config.error.errorContent}</span>
</div>
);
} else {
const renderedData: JSX.Element[] = [];
if (config.data.config.locationRules) {
renderedData.push(
...Object.keys(config.data.config.locationRules).map((key) => (
<AppCard
key={key}
type="native"
info={{
name: key,
config: config.data.config.vhostOptions
? config.data.config.vhostOptions[key]
: {},
}}
rule={true}
/>
))
);
}
if (config.data.config.samlSPMetaDataXML) {
renderedData.push(
...Object.keys(config.data.config.samlSPMetaDataXML).map((key) => (
<AppCard
key={key}
type="SPsaml"
info={{
name: key,
config: config.data.config.samlSPMetaDataXML
? config.data.config.samlSPMetaDataXML[key]
: {},
}}
issuer={config.data.config.issuerDBSAMLActivation}
rule={ruleSAML(
config.data.config.samlSPMetaDataXML
? config.data.config.samlSPMetaDataXML[key]
: {}
)}
/>
))
);
}
if (config.data.config.oidcRPMetaDataOptions) {
renderedData.push(
...Object.keys(config.data.config.oidcRPMetaDataOptions).map(
(key) => (
<AppCard
key={key}
type="RPoidc"
info={{
name: key,
config: config.data.config.oidcRPMetaDataOptions
? config.data.config.oidcRPMetaDataOptions[key]
: {},
}}
issuer={config.data.config.issuerDBOpenIDConnectActivation}
rule={ruleOIDC(
config.data.config.oidcRPMetaDataOptions
? config.data.config.oidcRPMetaDataOptions[key]
: {}
)}
/>
)
)
);
}
if (config.data.config.casAppMetaDataOptions) {
renderedData.push(
...Object.keys(config.data.config.casAppMetaDataOptions).map(
(key) => (
<AppCard
key={key}
type="AppCas"
info={{
name: key,
config: config.data.config.casAppMetaDataOptions
? config.data.config.casAppMetaDataOptions[key]
: {},
}}
issuer={config.data.config.issuerDBCASActivation}
rule={ruleCAS(
config.data.config.casAppMetaDataOptions
? config.data.config.casAppMetaDataOptions[key]
: {}
)}
/>
)
)
);
}
renderedData.filter((el) => {
return String(el.props.info.name).includes(filters.search);
});

if (filters.alpha) {
renderedData.sort((el1, el2) =>
el1.props.info.name > el2.props.info.name ? 1 : -1
);
}
const pageLimit = 12;
const pageNb = Math.ceil(renderedData.length / pageLimit);
const pages = Array.from(
{ length: Math.ceil(renderedData.length / pageLimit) },
(v, i) => renderedData.slice(i * pageLimit, i * pageLimit + pageLimit)
);
const handleChangePage = (event: ChangeEvent<unknown>, value: number) => {
setPage(value);
};

return (
<>
<strong className="title"> {t("currentConfiguration")}</strong>
<Button
variant="contained"
sx={{ verticalAlign: "top" }}
className="cfgNum"
color={config.data.metadata.next ? "warning" : "success"}
onClick={(e) => {
handleClick(e);
}}
>
{config.data.metadata.cfgNum}
</Button>
{false && <Button
onClick={() => {
dispatch(push("#catandapp"));
}}
>
<WidgetsOutlinedIcon color="secondary" />
</Button>}
{false && <Issuers />}
<FilterToggle filters={filters} setFilters={setFilters} />
<Pagination
sx={{
justifyContent: "center",
display: "flex",
}}
count={pageNb}
page={page}
onChange={handleChangePage}
color="primary"
size="large"
showFirstButton
showLastButton
/>
<div className="grid">{pages[page - 1]}</div>
<Pagination
sx={{
justifyContent: "center",
display: "flex",
margin: "15px",
}}
count={pageNb}
page={page}
onChange={handleChangePage}
color="primary"
size="large"
showFirstButton
showLastButton
/>
<Menu
id="del-menu"
anchorEl={anchorEl}
open={anchorEl ? true : false}
onClose={() => {
setAnchorEl(null);
}}
>
<MenuItem
onClick={() => {
dispatch(push(`#conf/${config.data.metadata.prev}`));
setAnchorEl(null);
}}
>
<ArrowBackIcon sx={{ marginRight: "15px" }} />
{t("previous")}
</MenuItem>
<Divider />
<MenuItem
disabled={!config.data.metadata.next}
onClick={() => {
dispatch(push(`#conf/${config.data.metadata.next}`));
setAnchorEl(null);
}}
>
<ArrowForwardIcon sx={{ marginRight: "15px" }} />
{t("next")}
</MenuItem>
<Divider />
<MenuItem
onClick={() => {
dispatch(push(`#conf/latest`));
setAnchorEl(null);
}}
>
<CachedIcon sx={{ marginRight: "15px" }} />
{t("latest")}
</MenuItem>
</Menu>
</>
);
}
} catch (e) {
console.debug(e);
if (e instanceof Error) {
dispatch(setError(`${e.name} : ${e.message}`));
dispatch(removeError());
}
return <div className="main">{config.error.errorContent}</div>;
}
}
Loading

0 comments on commit 6f45a1e

Please sign in to comment.