mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-01-26 01:21:04 +00:00
Add onboarding steps (#150)
This commit is contained in:
1177
package-lock.json
generated
1177
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@axa-fr/react-oidc": "^5.14.0",
|
||||
"@headlessui/react": "^1.5.0",
|
||||
"@heroicons/react": "^1.0.4",
|
||||
@@ -18,7 +18,7 @@
|
||||
"@types/react-redux": "^7.1.24",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"antd": "^4.20.6",
|
||||
"antd": "^5.3.1",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"axios": "^0.27.2",
|
||||
"cidr-regex": "^3.1.1",
|
||||
@@ -27,11 +27,13 @@
|
||||
"highlight.js": "^11.2.0",
|
||||
"history": "^5.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"postcss": "^8.4.12",
|
||||
"prop-types": "^15.7.2",
|
||||
"punycode": "^2.1.1",
|
||||
"rc-overflow": "^1.2.8",
|
||||
"react": "^18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-hotjar": "^5.1.0",
|
||||
"react-redux": "^8.0.2",
|
||||
|
||||
95
src/App.tsx
95
src/App.tsx
@@ -4,7 +4,7 @@ import {apiClient, store} from "./store";
|
||||
import {hotjar} from 'react-hotjar';
|
||||
import {getConfig} from "./config";
|
||||
import Banner from "./components/Banner";
|
||||
import {Col, Layout, Row} from "antd";
|
||||
import {Col, ConfigProvider, Layout, Row} from "antd";
|
||||
import {Container} from "./components/Container";
|
||||
import Navbar from "./components/Navbar";
|
||||
import {Redirect, Route, Switch} from "react-router-dom";
|
||||
@@ -24,7 +24,6 @@ import Activity from "./views/Activity";
|
||||
import Settings from "./views/Settings";
|
||||
|
||||
|
||||
|
||||
const {Header, Content} = Layout;
|
||||
|
||||
function App() {
|
||||
@@ -58,7 +57,7 @@ function App() {
|
||||
run.current = true
|
||||
apiClient.request<User[]>('GET', `/api/users`, {getAccessTokenSilently: getAccessTokenSilently})
|
||||
.then(() => {
|
||||
setShow(true)
|
||||
setShow(true)
|
||||
})
|
||||
.catch(e => {
|
||||
setShow(true)
|
||||
@@ -70,51 +69,61 @@ function App() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
token: {
|
||||
borderRadius: 2,
|
||||
colorPrimary: "#1890ff",
|
||||
fontFamily: "Arial"
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Provider store={store}>
|
||||
{!show && <SecureLoading padding="3em" width={50} height={50}/>}
|
||||
{show &&
|
||||
<Layout>
|
||||
<Banner/>
|
||||
<Header className="header" style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
alignContent: "center"
|
||||
}}>
|
||||
<Row justify="space-around" align="middle">
|
||||
<Col span={24}>
|
||||
<Container>
|
||||
<Navbar/>
|
||||
</Container>
|
||||
</Col>
|
||||
</Row>
|
||||
</Header>
|
||||
<Content style={{minHeight: "100vh"}}>
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path="/"
|
||||
render={() => {
|
||||
return (
|
||||
<Redirect to="/peers"/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Route path='/peers' exact component={withOidcSecure(Peers)}/>
|
||||
<Route path="/add-peer" component={withOidcSecure(AddPeer)}/>
|
||||
<Route path="/setup-keys" component={withOidcSecure(SetupKeys)}/>
|
||||
<Route path="/acls" component={withOidcSecure(AccessControl)}/>
|
||||
<Route path="/routes" component={withOidcSecure(Routes)}/>
|
||||
<Route path="/users" component={withOidcSecure(Users)}/>
|
||||
<Route path="/dns" component={withOidcSecure(DNS)}/>
|
||||
<Route path="/activity" component={withOidcSecure(Activity)}/>
|
||||
<Route path="/settings" component={withOidcSecure(Settings)}/>
|
||||
</Switch>
|
||||
</Content>
|
||||
<FooterComponent/>
|
||||
</Layout>
|
||||
<Layout>
|
||||
<Banner/>
|
||||
<Header className="header" style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
alignContent: "center"
|
||||
}}>
|
||||
<Row justify="space-around" align="middle">
|
||||
<Col span={24}>
|
||||
<Container>
|
||||
<Navbar/>
|
||||
</Container>
|
||||
</Col>
|
||||
</Row>
|
||||
</Header>
|
||||
<Content style={{minHeight: "100vh"}}>
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path="/"
|
||||
render={() => {
|
||||
return (
|
||||
<Redirect to="/peers"/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Route path='/peers' exact component={withOidcSecure(Peers)}/>
|
||||
<Route path="/setup-keys" component={withOidcSecure(SetupKeys)}/>
|
||||
<Route path="/acls" component={withOidcSecure(AccessControl)}/>
|
||||
<Route path="/routes" component={withOidcSecure(Routes)}/>
|
||||
<Route path="/users" component={withOidcSecure(Users)}/>
|
||||
<Route path="/dns" component={withOidcSecure(DNS)}/>
|
||||
<Route path="/activity" component={withOidcSecure(Activity)}/>
|
||||
<Route path="/settings" component={withOidcSecure(Settings)}/>
|
||||
</Switch>
|
||||
</Content>
|
||||
<FooterComponent/>
|
||||
</Layout>
|
||||
}
|
||||
</Provider>
|
||||
|
||||
</ConfigProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ const Navbar = () => {
|
||||
|
||||
const items = [
|
||||
{label: (<Link to="/peers">Peers</Link>), key: '/peers'},
|
||||
{label: (<Link to="/add-peer">Add Peer</Link>), key: '/add-peer'},
|
||||
{label: (<Link to="/setup-keys">Setup Keys</Link>), key: '/setup-keys'},
|
||||
{label: (<Link to="/acls">Access Control</Link>), key: '/acls'},
|
||||
{label: (<Link to="/routes">Network Routes</Link>), key: '/routes'},
|
||||
|
||||
@@ -29,6 +29,7 @@ import {CustomTagProps} from "rc-select/lib/BaseSelect";
|
||||
import {Group} from "../store/group/types";
|
||||
import {useGetAccessTokenSilently} from "../utils/token";
|
||||
import ExpiresInInput, {expiresInToSeconds, ExpiresInValue} from "../views/ExpiresInInput";
|
||||
import moment from "moment";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
@@ -41,7 +42,8 @@ const customExpiresFormat: DatePickerProps['format'] = value => {
|
||||
}
|
||||
|
||||
const customLastUsedFormat: DatePickerProps['format'] = value => {
|
||||
if (value.toString().startsWith("0001")) {
|
||||
if (value.year() == 1) {
|
||||
// 1st of Jan 0001
|
||||
return "never"
|
||||
}
|
||||
let ago = timeAgo(value.toString())
|
||||
@@ -93,7 +95,9 @@ const SetupKeyNew = () => {
|
||||
const fSetupKey = {
|
||||
...setupKey,
|
||||
autoGroupNames: setupKey.auto_groups ? formKeyGroups : [],
|
||||
expiresInFormatted: ExpiresInDefault
|
||||
expiresInFormatted: ExpiresInDefault,
|
||||
exp: moment(setupKey.expires),
|
||||
last: moment(setupKey.last_used)
|
||||
} as FormSetupKey
|
||||
setFormSetupKey(fSetupKey)
|
||||
form.setFieldsValue(fSetupKey)
|
||||
@@ -352,7 +356,7 @@ const SetupKeyNew = () => {
|
||||
{setupKey.id && formSetupKey.name &&
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="expires"
|
||||
name="exp"
|
||||
label="Expires"
|
||||
tooltip="The expiration date of the key"
|
||||
>
|
||||
@@ -365,7 +369,7 @@ const SetupKeyNew = () => {
|
||||
{setupKey.id && formSetupKey.name &&
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="last_used"
|
||||
name="last"
|
||||
label="Last Used"
|
||||
tooltip="The last time the key was used"
|
||||
>
|
||||
|
||||
97
src/components/addpeer/AddPeerPopup.tsx
Normal file
97
src/components/addpeer/AddPeerPopup.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import React, {useState} from 'react';
|
||||
|
||||
import {Tabs, TabsProps} from "antd";
|
||||
import Icon, {AndroidFilled, AppleFilled, WindowsFilled} from "@ant-design/icons";
|
||||
import {ReactComponent as LinuxSVG} from "../icons/terminal_icon.svg";
|
||||
import UbuntuTab from "./UbuntuTab";
|
||||
import {ReactComponent as DockerSVG} from "../icons/docker_icon.svg";
|
||||
import Paragraph from "antd/lib/typography/Paragraph";
|
||||
import WindowsTab from "./WindowsTab";
|
||||
import MacTab from "./MacTab";
|
||||
import Link from "antd/lib/typography/Link";
|
||||
import DockerTab from "./DockerTab";
|
||||
|
||||
type Props = {
|
||||
greeting?: string;
|
||||
headline: string;
|
||||
};
|
||||
|
||||
const detectOSTab = () => {
|
||||
let os = 1;
|
||||
if (navigator.userAgent.indexOf("Win") !== -1) os = 2;
|
||||
if (navigator.userAgent.indexOf("Mac") !== -1) os = 3;
|
||||
if (navigator.userAgent.indexOf("X11") !== -1) os = 1;
|
||||
if (navigator.userAgent.indexOf("Linux") !== -1) os = 1
|
||||
return os
|
||||
}
|
||||
|
||||
export const AddPeerPopup: React.FC<Props> = ({
|
||||
greeting,
|
||||
headline,
|
||||
}) => {
|
||||
|
||||
const [openTab, setOpenTab] = useState(detectOSTab);
|
||||
|
||||
const items: TabsProps['items'] = [
|
||||
{
|
||||
key: "1",
|
||||
label: <span><Icon component={LinuxSVG}/>Ubuntu</span>,
|
||||
children: <UbuntuTab/>,
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
label: <span><WindowsFilled/>Windows</span>,
|
||||
children: <WindowsTab/>,
|
||||
},
|
||||
{
|
||||
key: "3",
|
||||
label: <span><AppleFilled/>macOS</span>,
|
||||
children: <MacTab/>,
|
||||
},
|
||||
/*{
|
||||
key: "4",
|
||||
label: <span><AndroidFilled/>Android</span>,
|
||||
children: <></>,
|
||||
},*/
|
||||
{
|
||||
key: "5",
|
||||
label: <span><Icon component={DockerSVG}/>Docker</span>,
|
||||
children: <DockerTab/>,
|
||||
}
|
||||
];
|
||||
|
||||
return <>
|
||||
{greeting && <Paragraph
|
||||
style={{textAlign: "center", whiteSpace: "pre-line", fontSize: "2em", marginBottom: -10}}>
|
||||
{greeting}
|
||||
</Paragraph>}
|
||||
<Paragraph
|
||||
style={{textAlign: "center", whiteSpace: "pre-line", fontSize: "2em"}}>
|
||||
{headline}
|
||||
</Paragraph>
|
||||
<Paragraph type={"secondary"}
|
||||
style={{
|
||||
marginTop: "-15px",
|
||||
textAlign: "center",
|
||||
whiteSpace: "pre-line",
|
||||
}}>
|
||||
To get started install NetBird and log in using your {"\n"} email account.
|
||||
</Paragraph>
|
||||
|
||||
<Tabs centered
|
||||
defaultActiveKey={openTab.toString()} tabPosition="top" animated={{inkBar: true, tabPane: false}}
|
||||
items={items}/>
|
||||
<Paragraph type={"secondary"}
|
||||
style={{
|
||||
marginTop: "15px",
|
||||
}}>
|
||||
After that you should be connected. Add more devices to your network or manage your existing devices in the
|
||||
admin panel.
|
||||
If you have further questions check out our {<Link target="_blank"
|
||||
href={"https://netbird.io/docs/getting-started/installation"}>installation
|
||||
guide</Link>}
|
||||
</Paragraph>
|
||||
</>
|
||||
}
|
||||
|
||||
export default AddPeerPopup
|
||||
63
src/components/addpeer/DockerTab.tsx
Normal file
63
src/components/addpeer/DockerTab.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import React, {useState} from 'react';
|
||||
import {StepCommand} from "./types"
|
||||
import {formatDockerCommand, formatNetBirdUP} from "./common"
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import TabSteps from "./TabSteps";
|
||||
import {Button, Typography} from "antd";
|
||||
import Link from "antd/lib/typography/Link";
|
||||
|
||||
const {Title, Paragraph, Text} = Typography;
|
||||
|
||||
export const DockerTab = () => {
|
||||
|
||||
const [steps, setSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: 'Install Docker',
|
||||
commands: (
|
||||
<Button style={{marginTop: "5px"}} type="primary" href="https://docs.docker.com/engine/install/" target="_blank">Official Docker website</Button>
|
||||
),
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: 'Run NetBird container',
|
||||
commands: formatDockerCommand(),
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 3,
|
||||
title: "Read docs",
|
||||
commands: (
|
||||
<Link href="https://netbird.io/docs/getting-started/installation#running-netbird-in-docker" target="_blank">Running NetBird in Docker</Link>
|
||||
),
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
} as StepCommand
|
||||
])
|
||||
|
||||
return (
|
||||
<div style={{marginTop: 10}}>
|
||||
{/*<Text style={{fontWeight: "bold"}}>
|
||||
Run in Docker
|
||||
</Text>
|
||||
<div style={{fontSize: ".85em", marginTop: 5, marginBottom: 25}}>
|
||||
<SyntaxHighlighter language="bash">
|
||||
{formatDockerCommand()}
|
||||
</SyntaxHighlighter>
|
||||
</div>*/}
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Install on Ubuntu
|
||||
</Text>
|
||||
<div style={{marginTop: 5}}>
|
||||
<TabSteps stepsItems={steps}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default DockerTab
|
||||
@@ -1,18 +1,49 @@
|
||||
import React, {useState} from 'react';
|
||||
|
||||
import { Button } from "antd";
|
||||
import {Button, Typography} from "antd";
|
||||
import TabSteps from "./TabSteps";
|
||||
import { StepCommand } from "./types"
|
||||
import { formatNetBirdUP } from "./common"
|
||||
import {StepCommand} from "./types"
|
||||
import {formatNetBirdUP} from "./common"
|
||||
import {Collapse} from "antd";
|
||||
const { Panel } = Collapse;
|
||||
|
||||
const {Text} = Typography;
|
||||
|
||||
export const LinuxTab = () => {
|
||||
|
||||
const [quickSteps, setQuickSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: 'Download and run installer:',
|
||||
commands: (
|
||||
<Button style={{marginTop: "5px"}} type="primary" href="https://pkgs.netbird.io/windows/x64"
|
||||
target="_blank">Download NetBird</Button>
|
||||
),
|
||||
copied: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: 'Click on "Connect" from the NetBird icon in your system tray',
|
||||
commands: '',
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
title: 'Sign up using your email address',
|
||||
commands: '',
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
}
|
||||
])
|
||||
|
||||
const [steps, setSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: 'Download and install Brew (package manager)',
|
||||
title: 'Download and install Homebrew',
|
||||
commands: (
|
||||
<Button type="primary" href="https://brew.sh/" target="_blank">Download Brew</Button>
|
||||
<Button style={{marginTop: "5px"}} type="primary" href="https://brew.sh/" target="_blank">Download
|
||||
Brew</Button>
|
||||
),
|
||||
copied: false
|
||||
} as StepCommand,
|
||||
@@ -44,20 +75,35 @@ export const LinuxTab = () => {
|
||||
commands: formatNetBirdUP(),
|
||||
copied: false,
|
||||
showCopyButton: true
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 5,
|
||||
title: 'Get your IP address:',
|
||||
commands: [
|
||||
`sudo ifconfig utun100`
|
||||
].join('\n'),
|
||||
copied: false,
|
||||
showCopyButton: true
|
||||
} as StepCommand
|
||||
])
|
||||
|
||||
return (
|
||||
<TabSteps stepsItems={steps}/>
|
||||
/*<div style={{marginTop: 10}}>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Install on macOS
|
||||
</Text>
|
||||
<div style={{marginTop: 5}}>
|
||||
<TabSteps stepsItems={quickSteps}/>
|
||||
</div>
|
||||
<Collapse style={{marginLeft: -15}} bordered={false}>
|
||||
<Panel header={<Text style={{fontWeight: "bold"}}>
|
||||
Or install manually with Homebrew
|
||||
</Text>} key="1">
|
||||
<div style={{marginTop: 5}}>
|
||||
<TabSteps stepsItems={steps}/>
|
||||
</div>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
</div>*/
|
||||
<div style={{marginTop: 10}}>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Install on macOS with Homebrew
|
||||
</Text>
|
||||
<div style={{marginTop: 5}}>
|
||||
<TabSteps stepsItems={steps}/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,16 @@ import "highlight.js/styles/mono-blue.css";
|
||||
import "highlight.js/lib/languages/bash";
|
||||
import { StepCommand } from './types'
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import { monoBlue } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import {
|
||||
Typography,
|
||||
Space,
|
||||
Steps, Button
|
||||
Steps, Button, Popover, StepsProps
|
||||
} from "antd";
|
||||
import {copyToClipboard} from "../../utils/common";
|
||||
import {CheckOutlined, CopyOutlined} from "@ant-design/icons";
|
||||
import React, {useEffect, useState} from "react";
|
||||
const { Step } = Steps;
|
||||
const {Text} = Typography;
|
||||
|
||||
type Props = {
|
||||
stepsItems: Array<StepCommand>
|
||||
@@ -36,35 +36,22 @@ const TabSteps:React.FC<Props> = ({stepsItems}) => {
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Steps direction="vertical" current={0}>
|
||||
<Steps direction="vertical" size={"small"}>
|
||||
{steps.map(c =>
|
||||
<Step
|
||||
status={"process"}
|
||||
key={c.key}
|
||||
title={c.title}
|
||||
title={<Text>{c.title}</Text>}
|
||||
description={
|
||||
<Space className="nb-code" direction="vertical" size="small" style={{display: "flex"}}>
|
||||
<Space className="nb-code" direction="vertical" size="small" style={{display: "flex", fontSize: ".85em"}}>
|
||||
{ (c.commands && (typeof c.commands === 'string')) ? (
|
||||
<SyntaxHighlighter language="bash" style={monoBlue}>
|
||||
<SyntaxHighlighter language="bash">
|
||||
{c.commands}
|
||||
</SyntaxHighlighter>
|
||||
) : (
|
||||
c.commands
|
||||
)}
|
||||
{ c.showCopyButton &&
|
||||
<>
|
||||
{ !c.copied ? (
|
||||
<Button type="text" size="large" className="btn-copy-code" icon={<CopyOutlined/>}
|
||||
style={{color: "rgb(107, 114, 128)"}}
|
||||
onClick={() => onCopyClick(c.key, c.commands, true)}/>
|
||||
): (
|
||||
<Button type="text" size="large" className="btn-copy-code" icon={<CheckOutlined/>}
|
||||
style={{color: "green"}}/>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Button } from "antd";
|
||||
import React, {useState} from 'react';
|
||||
import {StepCommand} from "./types"
|
||||
import {formatNetBirdUP} from "./common"
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import TabSteps from "./TabSteps";
|
||||
import { StepCommand } from "./types"
|
||||
import { getConfig } from "../../config";
|
||||
import Paragraph from 'antd/lib/skeleton/Paragraph';
|
||||
import { formatNetBirdUP } from "./common"
|
||||
|
||||
import {Typography} from "antd";
|
||||
|
||||
const {Title, Paragraph, Text} = Typography;
|
||||
|
||||
export const UbuntuTab = () => {
|
||||
|
||||
const [steps, setSteps] = useState([
|
||||
{
|
||||
key: 1,
|
||||
title: 'Add repository:',
|
||||
title: 'Add repository',
|
||||
commands: [
|
||||
`sudo apt install ca-certificates curl gnupg -y`,
|
||||
`curl -L https://pkgs.wiretrustee.com/debian/public.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/wiretrustee.gpg`,
|
||||
`echo 'deb https://pkgs.wiretrustee.com/debian stable main' | sudo tee /etc/apt/sources.list.d/wiretrustee.list`
|
||||
].join('\n'),
|
||||
copied: false,
|
||||
showCopyButton: true
|
||||
showCopyButton: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: 'Install NetBird:',
|
||||
title: 'Install NetBird',
|
||||
commands: [
|
||||
`sudo apt-get update`,
|
||||
`# for CLI only`,
|
||||
@@ -34,39 +32,39 @@ export const UbuntuTab = () => {
|
||||
`sudo apt-get install netbird-ui`
|
||||
].join('\n'),
|
||||
copied: false,
|
||||
showCopyButton: true
|
||||
showCopyButton: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 3,
|
||||
title: 'Run NetBird and log in the browser:',
|
||||
title: 'Run NetBird and log in the browser',
|
||||
commands: formatNetBirdUP(),
|
||||
copied: false,
|
||||
showCopyButton: true
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 4,
|
||||
title: 'Get your IP address:',
|
||||
commands: [
|
||||
`ip addr show wt0`
|
||||
].join('\n'),
|
||||
copied: false,
|
||||
showCopyButton: true
|
||||
} as StepCommand,
|
||||
showCopyButton: false
|
||||
} as StepCommand
|
||||
])
|
||||
|
||||
/*const clickTest = () => {
|
||||
steps.push({
|
||||
key: steps.length+1,
|
||||
title: `Test ${steps.length+1}`,
|
||||
commands: [`hi lorena!`].join('\n'),
|
||||
copied: false
|
||||
})
|
||||
console.log(steps)
|
||||
setSteps([...steps])
|
||||
}*/
|
||||
|
||||
return (
|
||||
<TabSteps stepsItems={steps} />
|
||||
<div style={{marginTop: 10}}>
|
||||
{/*<Text style={{fontWeight: "bold"}}>
|
||||
Install with one command
|
||||
</Text>
|
||||
<div style={{fontSize: ".85em", marginTop: 5, marginBottom: 25}}>
|
||||
<SyntaxHighlighter language="bash">
|
||||
curl -fsSL https://netbird.io/install.sh | sh
|
||||
</SyntaxHighlighter>
|
||||
</div>*/}
|
||||
{/*<Text style={{fontWeight: "bold"}}>*/}
|
||||
{/* Or install manually*/}
|
||||
{/*</Text>*/}
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Install on Ubuntu
|
||||
</Text>
|
||||
<div style={{marginTop: 5}}>
|
||||
<TabSteps stepsItems={steps}/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, {useState} from 'react';
|
||||
|
||||
import { Button } from "antd";
|
||||
import {Button, Typography} from "antd";
|
||||
import TabSteps from "./TabSteps";
|
||||
import { StepCommand } from "./types"
|
||||
const {Text} = Typography;
|
||||
|
||||
export const WindowsTab = () => {
|
||||
|
||||
@@ -11,20 +12,20 @@ export const WindowsTab = () => {
|
||||
key: 1,
|
||||
title: 'Download and run Windows installer:',
|
||||
commands: (
|
||||
<Button type="primary" href="https://pkgs.netbird.io/windows/x64" target="_blank">Download NetBird</Button>
|
||||
<Button style={{marginTop: "5px"}} type="primary" href="https://pkgs.netbird.io/windows/x64" target="_blank">Download NetBird</Button>
|
||||
),
|
||||
copied: false
|
||||
} as StepCommand,
|
||||
{
|
||||
key: 2,
|
||||
title: 'Click on "Connect" from the NetBird icon in your system tray.',
|
||||
title: 'Click on "Connect" from the NetBird icon in your system tray',
|
||||
commands: '',
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
title: 'Log in your browser.\n',
|
||||
title: 'Sign up using your email address',
|
||||
commands: '',
|
||||
copied: false,
|
||||
showCopyButton: false
|
||||
@@ -32,7 +33,15 @@ export const WindowsTab = () => {
|
||||
])
|
||||
|
||||
return (
|
||||
<TabSteps stepsItems={steps}/>
|
||||
<div style={{marginTop: 10}}>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
Install on Windows
|
||||
</Text>
|
||||
<div style={{marginTop: 5}}>
|
||||
<TabSteps stepsItems={steps}/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,23 @@ const { grpcApiOrigin } = getConfig();
|
||||
|
||||
|
||||
export const formatNetBirdUP = () => {
|
||||
let cmd = "sudo netbird up"
|
||||
let cmd = "netbird up"
|
||||
if (grpcApiOrigin) {
|
||||
cmd = "sudo netbird up --management-url " + grpcApiOrigin
|
||||
cmd = "netbird up --management-url " + grpcApiOrigin
|
||||
}
|
||||
return [
|
||||
cmd
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
export const formatDockerCommand = () => {
|
||||
let cmd = ["docker run --rm -d",
|
||||
" --cap-add=NET_ADMIN",
|
||||
" -e NB_SETUP_KEY=SETUP_KEY",
|
||||
" -v netbird-client:/etc/netbird"]
|
||||
if (grpcApiOrigin) {
|
||||
cmd.push(" -e NB_MANAGEMENT_URL="+grpcApiOrigin)
|
||||
}
|
||||
cmd.push(" netbirdio/netbird:latest")
|
||||
return cmd.join(' \\\n')
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import * as React from "react";
|
||||
|
||||
export interface StepCommand {
|
||||
key: number | string,
|
||||
title: string,
|
||||
title: React.ReactNode | string | null,
|
||||
commands: React.ReactNode | string | null,
|
||||
copied?: boolean,
|
||||
showCopyButton?: boolean
|
||||
|
||||
1
src/components/icons/docker_icon.svg
Normal file
1
src/components/icons/docker_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg data-name="Layer 1" id="Layer_1" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M507,211.16c-1.42-1.19-14.25-10.94-41.79-10.94a132.55,132.55,0,0,0-21.61,1.9c-5.22-36.4-35.38-54-36.57-55l-7.36-4.28-4.75,6.9a101.65,101.65,0,0,0-13.06,30.45c-5,20.7-1.9,40.2,8.55,56.85-12.59,7.14-33,8.8-37.28,9H15.94A15.93,15.93,0,0,0,0,262.07,241.25,241.25,0,0,0,14.75,348.9C26.39,379.35,43.72,402,66,415.74,91.22,431.2,132.3,440,178.6,440a344.23,344.23,0,0,0,62.45-5.71,257.44,257.44,0,0,0,81.69-29.73,223.55,223.55,0,0,0,55.57-45.67c26.83-30.21,42.74-64,54.38-94h4.75c29.21,0,47.26-11.66,57.23-21.65a63.31,63.31,0,0,0,15.2-22.36l2.14-6.18Z"/><path d="M47.29,236.37H92.4a4,4,0,0,0,4-4h0V191.89a4,4,0,0,0-4-4H47.29a4,4,0,0,0-4,4h0v40.44a4.16,4.16,0,0,0,4,4h0"/><path d="M109.5,236.37h45.12a4,4,0,0,0,4-4h0V191.89a4,4,0,0,0-4-4H109.5a4,4,0,0,0-4,4v40.44a4.16,4.16,0,0,0,4,4"/><path d="M172.9,236.37H218a4,4,0,0,0,4-4h0V191.89a4,4,0,0,0-4-4H172.9a4,4,0,0,0-4,4h0v40.44a3.87,3.87,0,0,0,4,4h0"/><path d="M235.36,236.37h45.12a4,4,0,0,0,4-4V191.89a4,4,0,0,0-4-4H235.36a4,4,0,0,0-4,4h0v40.44a4,4,0,0,0,4,4h0"/><path d="M109.5,178.57h45.12a4.16,4.16,0,0,0,4-4V134.09a4,4,0,0,0-4-4H109.5a4,4,0,0,0-4,4v40.44a4.34,4.34,0,0,0,4,4"/><path d="M172.9,178.57H218a4.16,4.16,0,0,0,4-4V134.09a4,4,0,0,0-4-4H172.9a4,4,0,0,0-4,4h0v40.44a4,4,0,0,0,4,4"/><path d="M235.36,178.57h45.12a4.16,4.16,0,0,0,4-4V134.09a4.16,4.16,0,0,0-4-4H235.36a4,4,0,0,0-4,4h0v40.44a4.16,4.16,0,0,0,4,4"/><path d="M235.36,120.53h45.12a4,4,0,0,0,4-4V76a4.16,4.16,0,0,0-4-4H235.36a4,4,0,0,0-4,4h0v40.44a4.17,4.17,0,0,0,4,4"/><path d="M298.28,236.37H343.4a4,4,0,0,0,4-4V191.89a4,4,0,0,0-4-4H298.28a4,4,0,0,0-4,4h0v40.44a4.16,4.16,0,0,0,4,4"/></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
src/components/icons/terminal_icon.svg
Normal file
1
src/components/icons/terminal_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><title/><path d="M432,32H80A64.07,64.07,0,0,0,16,96V416a64.07,64.07,0,0,0,64,64H432a64.07,64.07,0,0,0,64-64V96A64.07,64.07,0,0,0,432,32ZM96,256a16,16,0,0,1-10-28.49L150.39,176,86,124.49a16,16,0,1,1,20-25l80,64a16,16,0,0,1,0,25l-80,64A16,16,0,0,1,96,256Zm160,0H192a16,16,0,0,1,0-32h64a16,16,0,0,1,0,32Z"/></svg>
|
||||
|
After Width: | Height: | Size: 419 B |
@@ -1,8 +1,4 @@
|
||||
@import '~antd/dist/antd.css';
|
||||
|
||||
/*@tailwind base;*/
|
||||
/*@tailwind components;*/
|
||||
/*@tailwind utilities;*/
|
||||
@import 'antd/dist/reset.css';
|
||||
|
||||
body {
|
||||
font-size: 16px;
|
||||
@@ -92,20 +88,6 @@ body {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.ant-steps-item-tail::after {
|
||||
background-color: #1890ff !important;
|
||||
}
|
||||
|
||||
.ant-steps-item-icon {
|
||||
background: #1890ff !important;
|
||||
color: #ffffff !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ant-steps-icon {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.nb-code {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
@@ -135,4 +117,26 @@ body {
|
||||
.access-control.ant-drawer-subtitle {
|
||||
line-height: 22px;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.ant-steps-item-icon{
|
||||
background: #EBEBEB !important;
|
||||
border-color: #EBEBEB !important;
|
||||
}
|
||||
.ant-steps-icon-dot{
|
||||
background: #EBEBEB !important;
|
||||
border-color: #EBEBEB !important;
|
||||
}
|
||||
.ant-steps-icon {
|
||||
background: #EBEBEB !important;
|
||||
border: none;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.ant-steps-item-tail::after {
|
||||
background: #EBEBEB !important;
|
||||
}
|
||||
|
||||
.ant-steps-item-tail {
|
||||
border: none;
|
||||
}
|
||||
@@ -1,29 +1,30 @@
|
||||
export enum StorageKey {
|
||||
token
|
||||
token,
|
||||
hadFirstRun
|
||||
}
|
||||
|
||||
const setLocalItem = async <T>(key: StorageKey, value: T): Promise<void> => {
|
||||
try {
|
||||
localStorage.setItem(`@net-bird:${key}`, JSON.stringify(value));
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
try {
|
||||
localStorage.setItem(`@net-bird:${key}`, JSON.stringify(value));
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
const getLocalItem = async <T>(key: StorageKey): Promise<T | null> => {
|
||||
try {
|
||||
const item = localStorage.getItem(`@net-bird:${key}`);
|
||||
if (!item) {
|
||||
return null;
|
||||
try {
|
||||
const item = localStorage.getItem(`@net-bird:${key}`);
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(item) as T;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
return JSON.parse(item) as T;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
return null;
|
||||
return null;
|
||||
};
|
||||
|
||||
export {
|
||||
getLocalItem,
|
||||
setLocalItem
|
||||
getLocalItem,
|
||||
setLocalItem
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {ExpiresInValue} from "../../views/ExpiresInInput";
|
||||
import moment from "moment";
|
||||
|
||||
export interface SetupKey {
|
||||
expires: string;
|
||||
@@ -19,6 +20,8 @@ export interface SetupKey {
|
||||
export interface FormSetupKey extends SetupKey {
|
||||
autoGroupNames: string[]
|
||||
expiresInFormatted: ExpiresInValue
|
||||
exp: moment.Moment
|
||||
last: moment.Moment
|
||||
}
|
||||
|
||||
export interface SetupKeyToSave extends SetupKey
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
export const formatOS = (os) => {
|
||||
if (os.startsWith("windows 10")) {
|
||||
return "Windows 10";
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {capitalize, formatOS, timeAgo} from "../utils/common";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {RootState} from "typesafe-actions";
|
||||
import {actions as peerActions} from '../store/peer';
|
||||
import {actions as groupActions} from '../store/group';
|
||||
import {actions as routeActions} from '../store/route';
|
||||
import {Container} from "../components/Container";
|
||||
import {ExclamationCircleOutlined, ReloadOutlined,} from '@ant-design/icons';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
@@ -31,8 +32,6 @@ import {
|
||||
} from "antd";
|
||||
import {Peer, PeerDataTable} from "../store/peer/types";
|
||||
import {filter} from "lodash"
|
||||
import {capitalize, formatOS, timeAgo} from "../utils/common";
|
||||
import {ExclamationCircleOutlined} from "@ant-design/icons";
|
||||
import {Group, GroupPeer} from "../store/group/types";
|
||||
import PeerUpdate from "../components/PeerUpdate";
|
||||
import tableSpin from "../components/Spin";
|
||||
@@ -41,6 +40,8 @@ import {useGetAccessTokenSilently} from "../utils/token";
|
||||
import {actions as userActions} from "../store/user";
|
||||
import ButtonCopyMessage from "../components/ButtonCopyMessage";
|
||||
import {usePageSizeHelpers} from "../utils/pageSize";
|
||||
import AddPeerPopup from "../components/addpeer/AddPeerPopup";
|
||||
import {getLocalItem, setLocalItem, StorageKey} from "../services/local";
|
||||
|
||||
const {Title, Paragraph, Text} = Typography;
|
||||
const {Column} = Table;
|
||||
@@ -48,7 +49,7 @@ const {confirm} = Modal;
|
||||
|
||||
export const Peers = () => {
|
||||
|
||||
const {onChangePageSize,pageSizeOptions,pageSize} = usePageSizeHelpers()
|
||||
const {onChangePageSize, pageSizeOptions, pageSize} = usePageSizeHelpers()
|
||||
|
||||
const {getAccessTokenSilently} = useGetAccessTokenSilently()
|
||||
const dispatch = useDispatch()
|
||||
@@ -64,6 +65,7 @@ export const Peers = () => {
|
||||
const updatedPeer = useSelector((state: RootState) => state.peer.updatedPeer);
|
||||
const updateGroupsVisible = useSelector((state: RootState) => state.peer.updateGroupsVisible)
|
||||
const users = useSelector((state: RootState) => state.user.data);
|
||||
const [addPeerModalOpen, setAddPeerModalOpen] = useState(false);
|
||||
|
||||
const [textToSearch, setTextToSearch] = useState('');
|
||||
const [optionOnOff, setOptionOnOff] = useState('all');
|
||||
@@ -71,8 +73,8 @@ export const Peers = () => {
|
||||
const [peerToAction, setPeerToAction] = useState(null as PeerDataTable | null);
|
||||
const [groupPopupVisible, setGroupPopupVisible] = useState(false as boolean | undefined)
|
||||
const [showTutorial, setShowTutorial] = useState(false)
|
||||
|
||||
|
||||
const [hadFirstRun, setHadFirstRun] = useState(true)
|
||||
const [confirmModal, confirmModalContextHolder] = Modal.useModal();
|
||||
|
||||
const optionsOnOff = [{label: 'Online', value: 'on'}, {label: 'All', value: 'all'}]
|
||||
|
||||
@@ -89,7 +91,6 @@ export const Peers = () => {
|
||||
const actionsMenu = (<Menu items={itemsMenuAction}></Menu>)
|
||||
|
||||
const transformDataTable = (d: Peer[]): PeerDataTable[] => {
|
||||
const peer_ids = d.map(_p => _p.id)
|
||||
return d.map((p) => {
|
||||
const gs = groups
|
||||
.filter(g => g.peers?.find((_p: GroupPeer) => _p.id === p.id))
|
||||
@@ -103,16 +104,33 @@ export const Peers = () => {
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const refresh = () => {
|
||||
dispatch(userActions.getUsers.request({getAccessTokenSilently: getAccessTokenSilently, payload: null}));
|
||||
dispatch(peerActions.getPeers.request({getAccessTokenSilently: getAccessTokenSilently, payload: null}));
|
||||
dispatch(groupActions.getGroups.request({getAccessTokenSilently: getAccessTokenSilently, payload: null}));
|
||||
dispatch(routeActions.getRoutes.request({getAccessTokenSilently: getAccessTokenSilently, payload: null}));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getLocalItem<boolean>(StorageKey.hadFirstRun).then(f => setHadFirstRun(f === null? false : f))
|
||||
refresh()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!hadFirstRun) {
|
||||
setLocalItem(StorageKey.hadFirstRun, true).then()
|
||||
setAddPeerModalOpen(true)
|
||||
} else {
|
||||
setAddPeerModalOpen(false)
|
||||
}
|
||||
}, [hadFirstRun])
|
||||
|
||||
useEffect(() => {
|
||||
if (peers.length) {
|
||||
setShowTutorial(false)
|
||||
if (!hadFirstRun) {
|
||||
setHadFirstRun(true)
|
||||
}
|
||||
} else {
|
||||
setShowTutorial(true)
|
||||
}
|
||||
@@ -272,12 +290,11 @@ export const Peers = () => {
|
||||
</div>
|
||||
}
|
||||
let name = peerToAction ? peerToAction.name : ''
|
||||
confirm({
|
||||
confirmModal.confirm({
|
||||
icon: <ExclamationCircleOutlined/>,
|
||||
title: "Delete peer \"" + name + "\"",
|
||||
width: 600,
|
||||
content: contentModule,
|
||||
okType: 'danger',
|
||||
onOk() {
|
||||
dispatch(peerActions.deletedPeer.request({
|
||||
getAccessTokenSilently: getAccessTokenSilently,
|
||||
@@ -291,12 +308,11 @@ export const Peers = () => {
|
||||
}
|
||||
|
||||
const showConfirmEnableSSH = (record: PeerDataTable) => {
|
||||
confirm({
|
||||
confirmModal.confirm({
|
||||
icon: <ExclamationCircleOutlined/>,
|
||||
title: "Enable SSH Server for \"" + record.name + "\"?",
|
||||
width: 600,
|
||||
content: "Experimental feature. Enabling this option allows remote SSH access to this machine from other connected network participants.",
|
||||
okType: 'danger',
|
||||
onOk() {
|
||||
handleSwitchSSH(record, true)
|
||||
},
|
||||
@@ -384,7 +400,7 @@ export const Peers = () => {
|
||||
styleNotification={{}}/>
|
||||
}
|
||||
|
||||
const body = <span style={{height: "auto", whiteSpace: "normal", textAlign: "left"}}>
|
||||
const body = <span style={{textAlign: "left"}}>
|
||||
<Row>
|
||||
<ButtonCopyMessage keyMessage={peer.dns_label}
|
||||
toCopy={peer.dns_label}
|
||||
@@ -413,16 +429,19 @@ export const Peers = () => {
|
||||
if (!userEmail) {
|
||||
return <Button type="text" style={{height: "auto", whiteSpace: "normal", textAlign: "left"}}
|
||||
onClick={() => setUpdateGroupsVisible(peer, true)}>
|
||||
<Text strong>{peer.name}</Text>
|
||||
<span style={{textAlign: "left"}}>
|
||||
<Row><Text strong>{peer.name}</Text></Row>
|
||||
</span>
|
||||
</Button>
|
||||
}
|
||||
return <div>
|
||||
<Button type="text" style={{height: "auto", whiteSpace: "normal", textAlign: "left"}}
|
||||
<Button type="text"
|
||||
onClick={() => setUpdateGroupsVisible(peer, true)}>
|
||||
<Text strong>{peer.name}</Text>
|
||||
<br/>
|
||||
<Text type="secondary">{userEmail}</Text>
|
||||
{expiry}
|
||||
<span style={{textAlign: "left"}}>
|
||||
<Row> <Text strong>{peer.name}</Text></Row>
|
||||
<Row><Text type="secondary">{userEmail}</Text></Row>
|
||||
<Row> {expiry}</Row>
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
@@ -433,8 +452,10 @@ export const Peers = () => {
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Title level={4}>Peers</Title>
|
||||
<Paragraph>A list of all the machines in your account including their name, IP and
|
||||
status.</Paragraph>
|
||||
{showTutorial && <Paragraph type={"secondary"}>A list of all the machines in your account including their name, IP and
|
||||
status.</Paragraph>}
|
||||
{!showTutorial && <Paragraph>A list of all the machines in your account including their name, IP and
|
||||
status.</Paragraph>}
|
||||
<Space direction="vertical" size="large" style={{display: 'flex'}}>
|
||||
<Row gutter={[16, 24]}>
|
||||
<Col xs={24} sm={24} md={8} lg={8} xl={8} xxl={8} span={8}>
|
||||
@@ -449,8 +470,10 @@ export const Peers = () => {
|
||||
value={optionOnOff}
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
disabled={showTutorial}
|
||||
/>
|
||||
<Select value={pageSize.toString()} options={pageSizeOptions}
|
||||
disabled={showTutorial}
|
||||
onChange={onChangePageSize} className="select-rows-per-page-en"/>
|
||||
</Space>
|
||||
</Col>
|
||||
@@ -462,9 +485,7 @@ export const Peers = () => {
|
||||
xxl={5} span={5}>
|
||||
<Row justify="end">
|
||||
<Col>
|
||||
{!showTutorial &&
|
||||
<Link to="/add-peer" className="ant-btn ant-btn-primary ant-btn-block">Add
|
||||
Peer</Link>}
|
||||
{!showTutorial && <Button type="primary" onClick={() => setAddPeerModalOpen(true)}>Add peer</Button>}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
@@ -475,7 +496,7 @@ export const Peers = () => {
|
||||
closable/>
|
||||
}
|
||||
<Card bodyStyle={{padding: 0}}>
|
||||
<Table
|
||||
{!showTutorial && (<Table
|
||||
pagination={{
|
||||
pageSize,
|
||||
showSizeChanger: false,
|
||||
@@ -514,7 +535,8 @@ export const Peers = () => {
|
||||
<Tag color="red">offline</Tag>
|
||||
|
||||
if (record.login_expired) {
|
||||
return <Tooltip title="The peer is offline and needs to be re-authenticated because its login has expired ">
|
||||
return <Tooltip
|
||||
title="The peer is offline and needs to be re-authenticated because its login has expired ">
|
||||
<Tag color="orange">needs login</Tag>
|
||||
</Tooltip>
|
||||
|
||||
@@ -578,17 +600,23 @@ export const Peers = () => {
|
||||
}}></Dropdown.Button>
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
</Table>)}
|
||||
{showTutorial &&
|
||||
<Space direction="vertical" size="small" align="center"
|
||||
style={{display: 'flex', padding: '45px 15px', justifyContent: 'center'}}>
|
||||
<Paragraph type="secondary"
|
||||
<Title level={4}
|
||||
style={{textAlign: "center"}}>
|
||||
Get Started
|
||||
</Title>
|
||||
<Paragraph
|
||||
style={{textAlign: "center", whiteSpace: "pre-line"}}>
|
||||
It looks like you don't have any connected machines. {"\n"}
|
||||
Get started by adding one to your network!
|
||||
Get started by adding one to your network.
|
||||
</Paragraph>
|
||||
<Link to="/add-peer" className="ant-btn ant-btn-primary ant-btn-block">Add
|
||||
Peer</Link>
|
||||
<Button size={"middle"} type="primary" onClick={() => setAddPeerModalOpen(true)}>
|
||||
Add new peer
|
||||
</Button>
|
||||
|
||||
</Space>
|
||||
}
|
||||
</Card>
|
||||
@@ -597,6 +625,20 @@ export const Peers = () => {
|
||||
</Row>
|
||||
</Container>
|
||||
<PeerUpdate/>
|
||||
<Modal
|
||||
open={addPeerModalOpen}
|
||||
onOk={() => setAddPeerModalOpen(false)}
|
||||
onCancel={() => {
|
||||
setAddPeerModalOpen(false)
|
||||
setHadFirstRun(true)
|
||||
}}
|
||||
footer={[]}
|
||||
width={780}
|
||||
>
|
||||
{/* <AddPeerPopup greeting={"Hi there!"} headline={"It's time to add your first device."}/>*/}
|
||||
<AddPeerPopup greeting={!hadFirstRun ? "Hi there!" : ""} headline={!hadFirstRun ? "It's time to add your first device." : "Add new peer"}/>
|
||||
</Modal>
|
||||
{confirmModalContextHolder}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ export const SetupKeys = () => {
|
||||
|
||||
]
|
||||
const actionsMenu = (<Menu items={itemsMenuAction}></Menu>)
|
||||
const [confirmModal, confirmModalContextHolder] = Modal.useModal();
|
||||
|
||||
const transformDataTable = (d: SetupKey[]): SetupKeyDataTable[] => {
|
||||
return d.map(p => ({...p, groupsCount: p.auto_groups ? p.auto_groups.length : 0} as SetupKeyDataTable))
|
||||
@@ -176,7 +177,7 @@ export const SetupKeys = () => {
|
||||
}
|
||||
|
||||
const showConfirmRevoke = () => {
|
||||
confirm({
|
||||
confirmModal.confirm({
|
||||
icon: <ExclamationCircleOutlined/>,
|
||||
width: 600,
|
||||
content: <Space direction="vertical" size="small">
|
||||
@@ -187,7 +188,6 @@ export const SetupKeys = () => {
|
||||
</>
|
||||
}
|
||||
</Space>,
|
||||
okType: 'danger',
|
||||
onOk() {
|
||||
dispatch(setupKeyActions.saveSetupKey.request({
|
||||
getAccessTokenSilently: getAccessTokenSilently,
|
||||
@@ -462,6 +462,7 @@ export const SetupKeys = () => {
|
||||
|
||||
</Container>
|
||||
<SetupKeyNew/>
|
||||
{confirmModalContextHolder}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user