Skip to content

Commit 6b8b0d2

Browse files
Navbar reusable component (#2217)
* navbar reusable component * update RouteType * premium section Navbar * Installer and packages custom navigators * rm unecessary mb after section title * banner notifications mb
1 parent 375ddde commit 6b8b0d2

File tree

20 files changed

+371
-489
lines changed

20 files changed

+371
-489
lines changed

packages/admin-ui/src/components/NotificationsMain.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import RenderMarkdown from "components/RenderMarkdown";
44
import Button, { ButtonVariant } from "components/Button";
55
import { api, useApi } from "api";
66
import { Notification, Priority } from "@dappnode/types";
7-
import "./notificationsMain.scss";
87
import { MdClose } from "react-icons/md";
98
import { Accordion } from "react-bootstrap";
109
import { dappmanagerAliases, externalUrlProps } from "params";
1110
import { resolveDappnodeUrl } from "utils/resolveDappnodeUrl";
11+
import "./notificationsMain.scss";
1212

1313
/**
1414
* Displays banner notifications among all tabs
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from "react";
2+
import { Routes, Route, NavLink } from "react-router-dom";
3+
import { RouteType } from "types";
4+
import "./sectionNavigator.scss";
5+
6+
interface SectionNavbarProps {
7+
routes: RouteType[];
8+
hideNavbar?: boolean;
9+
}
10+
11+
export const SectionNavigator: React.FC<SectionNavbarProps> = ({ routes, hideNavbar = false }) => {
12+
return (
13+
<div>
14+
{/* Navbar */}
15+
{!hideNavbar && (
16+
<div className="horizontal-navbar">
17+
{routes
18+
.filter((route) => !route.hideSection)
19+
.map((route) => (
20+
<button key={route.subPath + "/*"} className="item-container">
21+
<NavLink to={route.subPath} className="item no-a-style" style={{ whiteSpace: "nowrap" }}>
22+
{route.name}
23+
</NavLink>
24+
</button>
25+
))}
26+
</div>
27+
)}
28+
29+
{/* Route render */}
30+
<div className="section-spacing">
31+
<Routes>
32+
{routes.map((r) => (
33+
<Route key={r.subPath} path={r.subPath} element={r.element} />
34+
))}
35+
</Routes>
36+
</div>
37+
</div>
38+
);
39+
};

packages/admin-ui/src/components/notificationsMain.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
display: flex;
33
flex-direction: column;
44
gap: 5px;
5+
margin-bottom: var(--default-spacing);
56

67
.banner-card {
78
padding: 10px 15px;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.horizontal-navbar {
2+
display: flex;
3+
margin-bottom: 0.5rem !important;
4+
.item-container {
5+
background-color: transparent;
6+
border: none;
7+
margin-bottom: 1rem;
8+
padding-bottom: 0.3rem;
9+
padding-left: 0;
10+
font-size: 1.2rem;
11+
&:not(:last-child) {
12+
padding-right: 1rem;
13+
margin-right: 1rem;
14+
border-right: var(--border-style);
15+
}
16+
&:hover {
17+
color: inherit;
18+
}
19+
20+
// Remove the border (outline) on click
21+
&:focus {
22+
outline: none;
23+
}
24+
}
25+
.item {
26+
color: #7b7d7f;
27+
border: none;
28+
padding-bottom: 0.3rem;
29+
cursor: pointer;
30+
&.active {
31+
color: #212529;
32+
border-bottom: 5px solid var(--dappnode-strong-main-color);
33+
}
34+
}
35+
36+
@media (max-width: 40rem) {
37+
overflow-x: scroll;
38+
}
39+
}

packages/admin-ui/src/layout.scss

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,6 @@ body,
158158
padding-top: var(--topbar-height) !important;
159159
margin-left: var(--sidenav-width);
160160
padding: var(--container-padding);
161-
162-
> div {
163-
margin-bottom: var(--default-spacing);
164-
}
165161
}
166162

167163
/********************
@@ -190,7 +186,7 @@ body,
190186
margin-left: var(--sidenav-collapsed-width);
191187
}
192188

193-
.card-body{
189+
.card-body {
194190
padding: 0.7rem;
195191
}
196192
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// InstallerNavigator.tsx
2+
import React from "react";
3+
import { Routes, Route, NavLink, useLocation, useNavigate } from "react-router-dom";
4+
import { SectionNavigator } from "components/SectionNavigator";
5+
import InstallDnpContainer from "./InstallDnpContainer";
6+
import { InstallerDnp } from "./dappnodeDappstore/InstallerDnp";
7+
import { InstallerPublic } from "./publicDappstore/InstallerPublic";
8+
import { confirm } from "components/ConfirmDialog";
9+
import { subPaths } from "../data";
10+
import "./installer.scss";
11+
import { RouteType } from "types";
12+
13+
type NavRoute = {
14+
name: string;
15+
subPath: string;
16+
link: string;
17+
};
18+
19+
const routesForNavbar: NavRoute[] = [
20+
{ name: "Dnp", subPath: subPaths.dnp, link: "dnp" },
21+
{ name: "Public", subPath: subPaths.public, link: "public" }
22+
];
23+
24+
export const InstallerNavigator: React.FC = () => {
25+
const navigate = useNavigate();
26+
const location = useLocation();
27+
28+
// Do not show the horizontal navbar in the following paths: /installer/dnp/:id/* and /installer/public/:id/*
29+
// it looks weird and it is not needed
30+
const hideNavbarPattern = /\/installer\/(dnp|public)\/.+/;
31+
const hideNavbar = hideNavbarPattern.test(location.pathname);
32+
33+
async function confirmPublicDappstore(e: React.MouseEvent) {
34+
e.preventDefault();
35+
try {
36+
await new Promise<void>((resolve, reject) =>
37+
confirm({
38+
title: `Are you sure you want to see the public repository?`,
39+
text: `The public repository is open and permissionless and can contain malicious packages that can compromise the security of your DAppNode. ONLY use the public repo if you know what you are doing and ONLY install packages whose developer you trust.
40+
41+
Nobody, DAppNode Association, DAppNodeDAO or anyone, will be held responsible for loss of funds, the compromising of the hardware or any other non intended consequences of installing a non-curated package.`,
42+
label: "Public DAppStore",
43+
buttons: [
44+
{ variant: "dappnode", label: "Cancel", onClick: () => reject() },
45+
{ variant: "danger", label: "I understand, take me to the public repo", onClick: () => resolve() }
46+
]
47+
})
48+
);
49+
navigate("/installer/public");
50+
} catch {
51+
// user cancelled
52+
}
53+
}
54+
55+
const sectionRoutes: RouteType[] = [
56+
{
57+
name: "Dnp",
58+
subPath: subPaths.dnp,
59+
element: (
60+
<Routes>
61+
<Route index element={<InstallerDnp />} />
62+
<Route path=":id/*" element={<InstallDnpContainer />} />
63+
</Routes>
64+
)
65+
},
66+
{
67+
name: "Public",
68+
subPath: subPaths.public,
69+
element: (
70+
<Routes>
71+
<Route index element={<InstallerPublic />} />
72+
<Route path=":id/*" element={<InstallDnpContainer />} />
73+
</Routes>
74+
)
75+
}
76+
];
77+
78+
return (
79+
<>
80+
{/* Custom navbar with guard for "Public" */}
81+
{!hideNavbar && (
82+
<div className="horizontal-navbar">
83+
{routesForNavbar.map((r) => {
84+
const guard = r.link === "public" ? confirmPublicDappstore : undefined;
85+
return (
86+
<button key={r.subPath} className="item-container" onClick={guard}>
87+
<NavLink to={r.link} className="item no-a-style" style={{ whiteSpace: "nowrap" }} onClick={guard}>
88+
{r.name}
89+
</NavLink>
90+
</button>
91+
);
92+
})}
93+
</div>
94+
)}
95+
96+
{/* Reuse SectionNavigator */}
97+
<SectionNavigator routes={sectionRoutes} hideNavbar />
98+
</>
99+
);
100+
};

packages/admin-ui/src/pages/installer/components/InstallerRoot.tsx

Lines changed: 3 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,16 @@
11
import React from "react";
2-
import { Routes, Route, NavLink, useNavigate, useLocation } from "react-router-dom";
32
// Components
4-
import InstallDnpContainer from "./InstallDnpContainer";
5-
import { title, subPaths } from "../data";
3+
import { title } from "../data";
64
import Title from "components/Title";
7-
import { InstallerDnp } from "./dappnodeDappstore/InstallerDnp";
8-
import { InstallerPublic } from "./publicDappstore/InstallerPublic";
9-
import { confirm } from "components/ConfirmDialog";
105
// Styles
116
import "./installer.scss";
7+
import { InstallerNavigator } from "./InstallerNavigator";
128

139
const InstallerRoot: React.FC = () => {
14-
const navigate = useNavigate();
15-
const location = useLocation();
16-
17-
const routes: {
18-
name: string;
19-
subPath: string;
20-
subLink: string;
21-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
22-
component: React.ComponentType<any>;
23-
}[] = [
24-
{
25-
name: "Dnp",
26-
subPath: subPaths.dnp,
27-
subLink: "dnp",
28-
component: () => <InstallerDnp />
29-
},
30-
{
31-
name: "Public",
32-
subPath: subPaths.public,
33-
subLink: "public",
34-
component: () => <InstallerPublic />
35-
}
36-
];
37-
async function confirmPublicDappstore(e: React.MouseEvent) {
38-
e.preventDefault();
39-
try {
40-
await new Promise<void>((resolve, reject) =>
41-
confirm({
42-
title: `Are you sure you want to see the public repository?`,
43-
text: `The public repository is open and permissionless and can contain malicious packages that can compromise the security of your DAppNode. ONLY use the public repo if you know what you are doing and ONLY install packages whose developer you trust.
44-
45-
Nobody, DAppNode Association, DAppNodeDAO or anyone, will be held responsible for loss of funds, the compromising of the hardware or any other non intended consequences of installing a non-curated package.`,
46-
label: "Public DAppStore",
47-
buttons: [
48-
{
49-
variant: "dappnode",
50-
label: "Cancel",
51-
onClick: () => reject()
52-
},
53-
{
54-
variant: "danger",
55-
label: "I understand, take me to the public repo",
56-
onClick: () => resolve()
57-
}
58-
]
59-
})
60-
);
61-
navigate("/installer/public");
62-
63-
} catch (e) {
64-
// do nothing
65-
}
66-
}
67-
68-
// Do not show the horizontal navbar in the following paths: /installer/dnp/:id/* and /installer/public/:id/*
69-
// it looks weird and it is not needed
70-
const hideNavbarPattern = /\/installer\/(dnp|public)\/.+/;
71-
const hideNavbar = hideNavbarPattern.test(location.pathname);
72-
7310
return (
7411
<>
7512
<Title title={title} />
76-
77-
{!hideNavbar && (
78-
<div className="horizontal-navbar">
79-
{routes.map((option) => (
80-
<button
81-
key={option.subPath}
82-
className="item-container"
83-
onClick={option.subLink === "public" ? confirmPublicDappstore : undefined}
84-
>
85-
<NavLink
86-
to={option.subLink}
87-
className="item no-a-style"
88-
style={{
89-
whiteSpace: "nowrap"
90-
}}
91-
onClick={option.subLink === "public" ? confirmPublicDappstore : undefined}
92-
>
93-
{option.name}
94-
</NavLink>
95-
</button>
96-
))}
97-
</div>
98-
)}
99-
100-
<Routes>
101-
{routes.map((route) => (
102-
<Route
103-
key={route.subPath}
104-
path={route.subPath}
105-
element={
106-
<Routes>
107-
<Route index element={<route.component />} />
108-
<Route path=":id/*" element={<InstallDnpContainer />} />
109-
</Routes>
110-
}
111-
/>
112-
))}
113-
</Routes>
13+
<InstallerNavigator />
11414
</>
11515
);
11616
};

0 commit comments

Comments
 (0)