mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
43
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c88bfa6476 | ||
|
|
c4138a8c45 | ||
|
|
3f5418bebc | ||
|
|
f60605e5e3 | ||
|
|
c1b2ededa7 | ||
|
|
31eaa4a241 | ||
|
|
ba365336ff | ||
|
|
06c238b013 | ||
|
|
ed56c240f3 | ||
|
|
1f3c7d87d7 | ||
|
|
9de2906fb2 | ||
|
|
359b443326 | ||
|
|
ecae39d94b | ||
|
|
30b858c1bc | ||
|
|
e82b269ff5 | ||
|
|
23a4b79f01 | ||
|
|
cc5a9b1033 | ||
|
|
09ae157be3 | ||
|
|
cb89eeb921 | ||
|
|
79446c0e77 | ||
|
|
1f258e4e2c | ||
|
|
bae95d2e11 | ||
|
|
3d95a826e7 | ||
|
|
d8d13aff01 | ||
|
|
695b571a50 | ||
|
|
ddfb6a6179 | ||
|
|
8c94119c6a | ||
|
|
439e803ef2 | ||
|
|
440c51a35c | ||
|
|
800e94de85 | ||
|
|
ba7d138156 | ||
|
|
a66fb3bf8f | ||
|
|
fcc51243e0 | ||
|
|
312c60dd45 | ||
|
|
09e6de74ee | ||
|
|
addd348456 | ||
|
|
a8190bfe5b | ||
|
|
9e3d9f245d | ||
|
|
e7a7a75906 | ||
|
|
67efd47f22 | ||
|
|
813cd851ca | ||
|
|
f44ccf3ef7 | ||
|
|
899f56acdc |
No files matched your search
+1
-1
@@ -26,6 +26,6 @@ yarn-error.log*
|
||||
src/auth_config.json
|
||||
.idea
|
||||
.eslintcache
|
||||
src/.local-config.json
|
||||
src/.local-config*.json
|
||||
/public/OidcServiceWorker.js
|
||||
/public/OidcTrustedDomains.js
|
||||
@@ -22,6 +22,10 @@ if [[ -z "${AUTH_AUDIENCE}" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${AUTH_AUDIENCE}" == "none" ]]; then
|
||||
unset AUTH_AUDIENCE
|
||||
fi
|
||||
|
||||
if [[ -z "${AUTH_SUPPORTED_SCOPES}" ]]; then
|
||||
if [[ -z "${AUTH0_DOMAIN}" ]]; then
|
||||
echo "AUTH_SUPPORTED_SCOPES environment variable must be set"
|
||||
|
||||
Generated
+471
-96
File diff suppressed because it is too large.
Load diff
@@ -39,6 +39,7 @@
|
||||
"react-redux": "^8.0.2",
|
||||
"react-router-dom": "^5.3.3",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-select": "^5.7.3",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-table": "^7.7.0",
|
||||
"redux": "^4.2.0",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"auth0Auth": "true",
|
||||
"authAuthority": "https://netbird-localdev.eu.auth0.com",
|
||||
"authClientId": "kBRMAOqIZ7hvpVCaypQLCJvTzkYYIXVt",
|
||||
"authScopesSupported": "openid profile email api offline_access email_verified",
|
||||
"authAudience": "http://localhost:3000/",
|
||||
|
||||
"apiOrigin": "http://0.0.0.0",
|
||||
"grpcApiOrigin": "http://0.0.0.0:80",
|
||||
"latestVersion": "v0.6.3"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="39" height="7" viewBox="0 0 39 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M38.3846 3.68889C38.5213 3.55221 38.5213 3.3306 38.3846 3.19392L36.1572 0.966532C36.0205 0.829849 35.7989 0.829849 35.6622 0.966532C35.5255 1.10322 35.5255 1.32482 35.6622 1.46151L37.6421 3.44141L35.6622 5.42131C35.5255 5.55799 35.5255 5.7796 35.6622 5.91628C35.7989 6.05296 36.0205 6.05296 36.1572 5.91628L38.3846 3.68889ZM0.703125 3.79141H38.1371V3.09141H0.703125V3.79141Z" fill="#D9D9D9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 503 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="39" height="7" viewBox="0 0 39 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.615425 3.3111C0.478741 3.44779 0.478741 3.66939 0.615425 3.80608L2.84281 6.03346C2.97949 6.17015 3.2011 6.17015 3.33778 6.03346C3.47447 5.89678 3.47447 5.67517 3.33778 5.53849L1.35789 3.55859L3.33778 1.57869C3.47447 1.44201 3.47447 1.2204 3.33778 1.08372C3.2011 0.947033 2.97949 0.947033 2.84281 1.08372L0.615425 3.3111ZM38.2969 3.20859L0.862911 3.20859L0.862911 3.90859L38.2969 3.90859L38.2969 3.20859Z" fill="#2B4DA5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 535 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="39" height="7" viewBox="0 0 39 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M38.3807 3.68889C38.5174 3.55221 38.5174 3.3306 38.3807 3.19392L36.1533 0.966532C36.0166 0.829849 35.795 0.829849 35.6583 0.966532C35.5216 1.10322 35.5216 1.32482 35.6583 1.46151L37.6382 3.44141L35.6583 5.42131C35.5216 5.55799 35.5216 5.7796 35.6583 5.91628C35.795 6.05296 36.0166 6.05296 36.1533 5.91628L38.3807 3.68889ZM0.699219 3.79141L38.1332 3.79141V3.09141L0.699219 3.09141V3.79141Z" fill="#2B4DA5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 517 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="39" height="7" viewBox="0 0 39 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M38.3846 3.68889C38.5213 3.55221 38.5213 3.3306 38.3846 3.19392L36.1572 0.966532C36.0205 0.829849 35.7989 0.829849 35.6622 0.966532C35.5255 1.10322 35.5255 1.32482 35.6622 1.46151L37.6421 3.44141L35.6622 5.42131C35.5255 5.55799 35.5255 5.7796 35.6622 5.91628C35.7989 6.05296 36.0205 6.05296 36.1572 5.91628L38.3846 3.68889ZM0.703125 3.79141H38.1371V3.09141H0.703125V3.79141Z" fill="#03543F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 503 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="39" height="7" viewBox="0 0 39 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.615425 3.3111C0.478741 3.44779 0.478741 3.66939 0.615425 3.80608L2.84281 6.03346C2.97949 6.17015 3.2011 6.17015 3.33778 6.03346C3.47447 5.89678 3.47447 5.67517 3.33778 5.53849L1.35789 3.55859L3.33778 1.57869C3.47447 1.44201 3.47447 1.2204 3.33778 1.08372C3.2011 0.947033 2.97949 0.947033 2.84281 1.08372L0.615425 3.3111ZM38.2969 3.20859L0.862911 3.20859L0.862911 3.90859L38.2969 3.90859L38.2969 3.20859Z" fill="#D9D9D9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 535 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="39" height="7" viewBox="0 0 39 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.615425 3.3111C0.478741 3.44779 0.478741 3.66939 0.615425 3.80608L2.84281 6.03346C2.97949 6.17015 3.2011 6.17015 3.33778 6.03346C3.47447 5.89678 3.47447 5.67517 3.33778 5.53849L1.35789 3.55859L3.33778 1.57869C3.47447 1.44201 3.47447 1.2204 3.33778 1.08372C3.2011 0.947033 2.97949 0.947033 2.84281 1.08372L0.615425 3.3111ZM38.2969 3.20859L0.862911 3.20859L0.862911 3.90859L38.2969 3.90859L38.2969 3.20859Z" fill="#03543F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 535 B |
File diff suppressed because it is too large.
Load diff
+537
-288
File diff suppressed because it is too large.
Load diff
@@ -6,7 +6,7 @@ const { Footer } = Layout
|
||||
export default () => {
|
||||
return (
|
||||
<Footer style={{ textAlign: 'center', bottom: "0"}}>
|
||||
Copyright © 2022 <a href="https://netbird.io">NetBird Authors</a>
|
||||
Copyright © 2023 <a href="https://netbird.io">NetBird Authors</a>
|
||||
</Footer>
|
||||
);
|
||||
};
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+225
-167
@@ -1,185 +1,243 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Link, useLocation} from 'react-router-dom';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import logo from "../assets/logo.png";
|
||||
import {Avatar, Button, Col, Dropdown, Grid, Menu, Row} from 'antd'
|
||||
import {ItemType} from "antd/lib/menu/hooks/useItems";
|
||||
import {AvatarSize} from "antd/es/avatar/SizeContext";
|
||||
import {UserOutlined} from '@ant-design/icons';
|
||||
import {useOidc, useOidcIdToken, useOidcUser} from '@axa-fr/react-oidc';
|
||||
import {getConfig} from "../config";
|
||||
import {User} from "../store/user/types";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {RootState} from "typesafe-actions";
|
||||
import {actions as userActions} from "../store/user";
|
||||
import {useGetTokenSilently} from "../utils/token";
|
||||
import {actions as personalAccessTokenActions} from "../store/personal-access-token";
|
||||
import { Avatar, Button, Col, Dropdown, Grid, Menu, Row } from "antd";
|
||||
import { ItemType } from "antd/lib/menu/hooks/useItems";
|
||||
import { AvatarSize } from "antd/es/avatar/SizeContext";
|
||||
import { UserOutlined } from "@ant-design/icons";
|
||||
import { useOidc, useOidcIdToken, useOidcUser } from "@axa-fr/react-oidc";
|
||||
import { getConfig } from "../config";
|
||||
import { User } from "../store/user/types";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "typesafe-actions";
|
||||
import { actions as userActions } from "../store/user";
|
||||
import { useGetTokenSilently } from "../utils/token";
|
||||
import { actions as personalAccessTokenActions } from "../store/personal-access-token";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
const { useBreakpoint } = Grid;
|
||||
|
||||
const Navbar = () => {
|
||||
let location = useLocation();
|
||||
const config = getConfig();
|
||||
const { logout } = useOidc();
|
||||
const {getTokenSilently} = useGetTokenSilently()
|
||||
const dispatch = useDispatch()
|
||||
let location = useLocation();
|
||||
const config = getConfig();
|
||||
const { logout } = useOidc();
|
||||
const { getTokenSilently } = useGetTokenSilently();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {oidcUser} = useOidcUser();
|
||||
const {idTokenPayload} = useOidcIdToken()
|
||||
const user = oidcUser;
|
||||
const [currentUser, setCurrentUser] = useState({} as User)
|
||||
const { oidcUser } = useOidcUser();
|
||||
const { idTokenPayload } = useOidcIdToken();
|
||||
const user = oidcUser;
|
||||
const [currentUser, setCurrentUser] = useState({} as User);
|
||||
|
||||
const screens = useBreakpoint();
|
||||
const screens = useBreakpoint();
|
||||
|
||||
const [hideMenuUser, setHideMenuUser] = useState(false)
|
||||
const users = useSelector((state: RootState) => state.user.data)
|
||||
const [isRefreshingUserState, setIsRefreshingUserState] = useState(false)
|
||||
const [hideMenuUser, setHideMenuUser] = useState(false);
|
||||
const users = useSelector((state: RootState) => state.user.data);
|
||||
const [isRefreshingUserState, setIsRefreshingUserState] = useState(false);
|
||||
|
||||
const items = [
|
||||
{label: (<Link to="/peers">Peers</Link>), key: '/peers'},
|
||||
{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'},
|
||||
{ label: (<Link to="/dns">DNS</Link>), key: '/dns' },
|
||||
{label: (<Link to="/users">Users</Link>), key: '/users'},
|
||||
{label: (<Link to="/activity">Activity</Link>), key: '/activity'},
|
||||
{label: (<Link to="/settings">Settings</Link>), key: '/settings'}
|
||||
] as ItemType[]
|
||||
const items = [
|
||||
{ label: <Link to="/peers">Peers</Link>, key: "/peers" },
|
||||
{ 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" },
|
||||
{ label: <Link to="/dns">DNS</Link>, key: "/dns" },
|
||||
{ label: <Link to="/users">Users</Link>, key: "/users" },
|
||||
{ label: <Link to="/activity">Activity</Link>, key: "/activity" },
|
||||
{ label: <Link to="/settings">Settings</Link>, key: "/settings" },
|
||||
] as ItemType[];
|
||||
|
||||
const userEmailKey = 'user-email'
|
||||
const userLogoutKey = 'user-logout'
|
||||
const userDividerKey = 'user-divider'
|
||||
const adminOnlyTabs = ["/setup-keys", "/acls", "/routes", "/dns", "/activity", "/settings"]
|
||||
const [menuItems, setMenuItems] = useState(items)
|
||||
const logoutWithRedirect = () =>
|
||||
logout("/", {client_id: config.clientId});
|
||||
const userEmailKey = "user-email";
|
||||
const userLogoutKey = "user-logout";
|
||||
const userDividerKey = "user-divider";
|
||||
const adminOnlyTabs = [
|
||||
"/setup-keys",
|
||||
"/acls",
|
||||
"/routes",
|
||||
"/dns",
|
||||
"/activity",
|
||||
"/settings",
|
||||
];
|
||||
const [menuItems, setMenuItems] = useState(items);
|
||||
const logoutWithRedirect = () => {
|
||||
let lRemove = [
|
||||
"peerFilter",
|
||||
"setupKeysFilter",
|
||||
"accessControlFilter",
|
||||
"routesFilter",
|
||||
"nameServerFilter",
|
||||
"userFilter",
|
||||
"serviceUserFilter",
|
||||
"activityFilter",
|
||||
];
|
||||
lRemove.forEach((element) => {
|
||||
localStorage.removeItem(element);
|
||||
});
|
||||
logout("/", { client_id: config.clientId });
|
||||
};
|
||||
|
||||
const openPersonalUserPage = () => {
|
||||
dispatch(userActions.setUser({
|
||||
id: currentUser.id,
|
||||
email: currentUser.email,
|
||||
role: currentUser.role,
|
||||
auto_groups: currentUser.auto_groups ? currentUser.auto_groups : [],
|
||||
name: currentUser.name,
|
||||
is_current: currentUser.is_current,
|
||||
is_service_user: currentUser.is_service_user,
|
||||
} as User));
|
||||
dispatch(userActions.setUserTabOpen("Users"));
|
||||
dispatch(userActions.setEditUserPopupVisible(true));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const fs = items.filter(m => showTab(m?.key?.toString(), currentUser) && m?.key !== userEmailKey && m?.key !== userLogoutKey && m?.key !== userDividerKey)
|
||||
if (screens.xs === true) {
|
||||
setHideMenuUser(false)
|
||||
fs.push({type: 'divider', key: userDividerKey})
|
||||
fs.push({
|
||||
label: (
|
||||
<Link to="#">{user?.name}</Link>
|
||||
),
|
||||
icon: createAvatar("small"),
|
||||
key: userEmailKey
|
||||
})
|
||||
fs.push({
|
||||
label: (<Button type="link" block onClick={logoutWithRedirect}>Logout</Button>),
|
||||
key: userLogoutKey
|
||||
})
|
||||
setMenuItems([...fs])
|
||||
return
|
||||
}
|
||||
setMenuItems([...fs])
|
||||
setHideMenuUser(true)
|
||||
}, [screens, currentUser])
|
||||
|
||||
useEffect(() => {
|
||||
if (users.length === 0 && !isRefreshingUserState &&
|
||||
window.location.pathname !== '/peers' &&
|
||||
window.location.pathname !== '/users') {
|
||||
setIsRefreshingUserState(true)
|
||||
dispatch(userActions.getUsers.request({getAccessTokenSilently: getTokenSilently, payload: null}))
|
||||
return
|
||||
}
|
||||
if (users.length === 0 && isRefreshingUserState) {
|
||||
return
|
||||
}
|
||||
let runUser = oidcUser
|
||||
if (!oidcUser) {
|
||||
runUser = idTokenPayload
|
||||
}
|
||||
setIsRefreshingUserState(false)
|
||||
if (runUser) {
|
||||
const found = users.find(u => u.is_current ? u.is_current : runUser.sub ? u.id == runUser.sub : false)
|
||||
if (found) {
|
||||
setCurrentUser(found)
|
||||
}
|
||||
}
|
||||
}, [users, oidcUser])
|
||||
|
||||
const showTab = (key: string | undefined, user: User | undefined) => {
|
||||
if (!user) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (user.role?.toLowerCase() === "admin") {
|
||||
return true
|
||||
}
|
||||
return !adminOnlyTabs.find(t => t === key)
|
||||
}
|
||||
|
||||
const menuUser = (
|
||||
<Menu
|
||||
items={[
|
||||
{
|
||||
label: (<Link to="/users" onClick={openPersonalUserPage}>{user?.email}</Link>),
|
||||
key: '0',
|
||||
},
|
||||
{
|
||||
label: (<Link to="/logout" onClick={logoutWithRedirect}>Logout</Link>),
|
||||
key: '1',
|
||||
}
|
||||
]}
|
||||
/>
|
||||
const openPersonalUserPage = () => {
|
||||
dispatch(
|
||||
userActions.setUser({
|
||||
id: currentUser.id,
|
||||
email: currentUser.email,
|
||||
role: currentUser.role,
|
||||
auto_groups: currentUser.auto_groups ? currentUser.auto_groups : [],
|
||||
name: currentUser.name,
|
||||
is_current: currentUser.is_current,
|
||||
is_service_user: currentUser.is_service_user,
|
||||
} as User)
|
||||
);
|
||||
dispatch(userActions.setUserTabOpen("Users"));
|
||||
dispatch(userActions.setEditUserPopupVisible(true));
|
||||
};
|
||||
|
||||
const createAvatar = (size: AvatarSize) => {
|
||||
return user?.picture ? (
|
||||
<Avatar size={size} src={user?.picture} icon={<UserOutlined/>}/>
|
||||
) : (
|
||||
<Avatar size={size}>{(user?.name || '').slice(0, 1).toUpperCase()}</Avatar>
|
||||
)
|
||||
useEffect(() => {
|
||||
const fs = items.filter(
|
||||
(m) =>
|
||||
showTab(m?.key?.toString(), currentUser) &&
|
||||
m?.key !== userEmailKey &&
|
||||
m?.key !== userLogoutKey &&
|
||||
m?.key !== userDividerKey
|
||||
);
|
||||
if (screens.xs === true) {
|
||||
setHideMenuUser(false);
|
||||
fs.push({ type: "divider", key: userDividerKey });
|
||||
fs.push({
|
||||
label: <Link to="#">{user?.name}</Link>,
|
||||
icon: createAvatar("small"),
|
||||
key: userEmailKey,
|
||||
});
|
||||
fs.push({
|
||||
label: (
|
||||
<Button type="link" block onClick={logoutWithRedirect}>
|
||||
Logout
|
||||
</Button>
|
||||
),
|
||||
key: userLogoutKey,
|
||||
});
|
||||
setMenuItems([...fs]);
|
||||
return;
|
||||
}
|
||||
setMenuItems([...fs]);
|
||||
setHideMenuUser(true);
|
||||
}, [screens, currentUser]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
users.length === 0 &&
|
||||
!isRefreshingUserState &&
|
||||
window.location.pathname !== "/peers" &&
|
||||
window.location.pathname !== "/users"
|
||||
) {
|
||||
setIsRefreshingUserState(true);
|
||||
dispatch(
|
||||
userActions.getUsers.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (users.length === 0 && isRefreshingUserState) {
|
||||
return;
|
||||
}
|
||||
let runUser = oidcUser;
|
||||
if (!oidcUser) {
|
||||
runUser = idTokenPayload;
|
||||
}
|
||||
setIsRefreshingUserState(false);
|
||||
if (runUser) {
|
||||
const found = users.find((u) =>
|
||||
u.is_current ? u.is_current : runUser.sub ? u.id == runUser.sub : false
|
||||
);
|
||||
if (found) {
|
||||
setCurrentUser(found);
|
||||
}
|
||||
}
|
||||
}, [users, oidcUser]);
|
||||
|
||||
const showTab = (key: string | undefined, user: User | undefined) => {
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row justify="space-evenly" align="middle">
|
||||
<Col flex="0 1 60px">
|
||||
<Link id="logo" to="/">
|
||||
<img
|
||||
alt="logo"
|
||||
style={{width: "55px"}}
|
||||
src={logo}
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col flex="1 1 auto">
|
||||
<div>
|
||||
<Menu mode="horizontal" selectable={true} selectedKeys={[location.pathname]}
|
||||
onSelect={(e) => {
|
||||
dispatch(userActions.setUser(null as unknown as User));
|
||||
dispatch(personalAccessTokenActions.resetPersonalAccessTokens(null))
|
||||
}}
|
||||
defaultSelectedKeys={[location.pathname]} items={menuItems}/>
|
||||
</div>
|
||||
</Col>
|
||||
{hideMenuUser &&
|
||||
<Col>
|
||||
<Dropdown overlay={menuUser} placement="bottomRight" trigger={['click']}>
|
||||
{createAvatar("large")}
|
||||
</Dropdown>
|
||||
</Col>
|
||||
}
|
||||
</Row>
|
||||
</>
|
||||
if (user.role?.toLowerCase() === "admin") {
|
||||
return true;
|
||||
}
|
||||
return !adminOnlyTabs.find((t) => t === key);
|
||||
};
|
||||
|
||||
const menuUser = (
|
||||
<Menu
|
||||
items={[
|
||||
{
|
||||
label: (
|
||||
<Link to="/users" onClick={openPersonalUserPage}>
|
||||
{user?.email}
|
||||
</Link>
|
||||
),
|
||||
key: "0",
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Link to="/logout" onClick={logoutWithRedirect}>
|
||||
Logout
|
||||
</Link>
|
||||
),
|
||||
key: "1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const createAvatar = (size: AvatarSize) => {
|
||||
return user?.picture ? (
|
||||
<Avatar size={size} src={user?.picture} icon={<UserOutlined />} />
|
||||
) : (
|
||||
<Avatar size={size}>
|
||||
{(user?.name || "").slice(0, 1).toUpperCase()}
|
||||
</Avatar>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row justify="space-evenly" align="middle">
|
||||
<Col flex="0 1 60px">
|
||||
<Link id="logo" to="/">
|
||||
<img alt="logo" style={{ width: "55px" }} src={logo} />
|
||||
</Link>
|
||||
</Col>
|
||||
<Col flex="1 1 auto">
|
||||
<div>
|
||||
<Menu
|
||||
mode="horizontal"
|
||||
selectable={true}
|
||||
selectedKeys={[location.pathname]}
|
||||
onSelect={(e) => {
|
||||
dispatch(userActions.setUser(null as unknown as User));
|
||||
dispatch(
|
||||
personalAccessTokenActions.resetPersonalAccessTokens(null)
|
||||
);
|
||||
}}
|
||||
defaultSelectedKeys={[location.pathname]}
|
||||
items={menuItems}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
{hideMenuUser && (
|
||||
<Col>
|
||||
<Dropdown
|
||||
overlay={menuUser}
|
||||
placement="bottomRight"
|
||||
trigger={["click"]}
|
||||
>
|
||||
{createAvatar("large")}
|
||||
</Dropdown>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
||||
+406
-144
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Loaded 20 of 65 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user