mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9875bb0fcd | ||
|
|
ae586b4dfe | ||
|
|
c0a6965310 | ||
|
|
f12a923c61 | ||
|
|
ad798bbab4 |
No files matched your search
Generated
-52
@@ -57,7 +57,6 @@
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/react-syntax-highlighter": "^15.5.3"
|
||||
}
|
||||
},
|
||||
@@ -3479,21 +3478,6 @@
|
||||
"url": "https://github.com/sponsors/gregberge"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz",
|
||||
"integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"postcss-selector-parser": "6.0.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.0.0 || insiders"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/dom": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.18.1.tgz",
|
||||
@@ -10562,23 +10546,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
@@ -19267,18 +19239,6 @@
|
||||
"loader-utils": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@tailwindcss/typography": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz",
|
||||
"integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"postcss-selector-parser": "6.0.10"
|
||||
}
|
||||
},
|
||||
"@testing-library/dom": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.18.1.tgz",
|
||||
@@ -24558,23 +24518,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/react-syntax-highlighter": "^15.5.3"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
'postcss-focus-visible': {
|
||||
replaceWith: '[data-focus-visible-added]',
|
||||
},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
+1
-1
@@ -22,7 +22,7 @@ import {SecureLoading} from "./components/Loading";
|
||||
import DNS from "./views/DNS";
|
||||
import Activity from "./views/Activity";
|
||||
import Settings from "./views/Settings";
|
||||
import './styles/tailwind.css'
|
||||
|
||||
|
||||
const {Header, Content} = Layout;
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
function SunIcon(props) {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" {...props}>
|
||||
<path d="M12.5 10a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0Z" />
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
d="M10 5.5v-1M13.182 6.818l.707-.707M14.5 10h1M13.182 13.182l.707.707M10 15.5v-1M6.11 13.889l.708-.707M4.5 10h1M6.11 6.111l.708.707"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function MoonIcon(props) {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" {...props}>
|
||||
<path d="M15.224 11.724a5.5 5.5 0 0 1-6.949-6.949 5.5 5.5 0 1 0 6.949 6.949Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ModeToggle() {
|
||||
function disableTransitionsTemporarily() {
|
||||
document.documentElement.classList.add('[&_*]:!transition-none')
|
||||
window.setTimeout(() => {
|
||||
document.documentElement.classList.remove('[&_*]:!transition-none')
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function toggleMode() {
|
||||
disableTransitionsTemporarily()
|
||||
|
||||
let isDarkMode = document.documentElement.classList.toggle('dark')
|
||||
|
||||
if (document.documentElement.getAttribute("data-dark-mode") !== "false") {
|
||||
delete window.localStorage.isDarkMode
|
||||
document.documentElement.setAttribute('data-dark-mode', 'false');
|
||||
} else {
|
||||
window.localStorage.isDarkMode = isDarkMode
|
||||
document.documentElement.setAttribute('data-dark-mode', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5"
|
||||
aria-label="Toggle dark mode"
|
||||
onClick={toggleMode}
|
||||
>
|
||||
<SunIcon className="h-5 w-5 stroke-zinc-900 hidden dark:block" />
|
||||
<MoonIcon className="dark:hidden h-5 w-5 stroke-white dark:block" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -314,7 +314,7 @@ const NameServerGroupUpdate = () => {
|
||||
// @ts-ignore
|
||||
const renderNSList = (fields: FormListFieldData[], {add, remove}, {errors}) => (
|
||||
<>
|
||||
<Row><label>Nameservers</label></Row>
|
||||
<Row>Nameservers</Row>
|
||||
{!!fields.length && (
|
||||
|
||||
<Row align='middle'>
|
||||
@@ -384,7 +384,7 @@ const NameServerGroupUpdate = () => {
|
||||
<Row>
|
||||
<Space>
|
||||
<Col>
|
||||
<label>Match domains</label>
|
||||
Match domains
|
||||
</Col>
|
||||
<Col>
|
||||
<Tooltip title="Only queries to domains specified here will be resolved by these nameservers."
|
||||
|
||||
@@ -13,7 +13,6 @@ 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 {ModeToggle} from "./ModeToggle";
|
||||
|
||||
const {useBreakpoint} = Grid;
|
||||
|
||||
@@ -171,9 +170,6 @@ const Navbar = () => {
|
||||
defaultSelectedKeys={[location.pathname]} items={menuItems}/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col style={{width: "3%"}}>
|
||||
<ModeToggle />
|
||||
</Col>
|
||||
{hideMenuUser &&
|
||||
<Col>
|
||||
<Dropdown overlay={menuUser} placement="bottomRight" trigger={['click']}>
|
||||
|
||||
@@ -292,7 +292,7 @@ const SetupKeyNew = () => {
|
||||
textAlign: "start",
|
||||
whiteSpace: "pre-line",
|
||||
marginTop: "-23px",
|
||||
paddingBottom: "15px",
|
||||
paddingBottom: "25px",
|
||||
}}>
|
||||
{"Use this key to register new machines in your network"}
|
||||
</Paragraph>
|
||||
@@ -323,7 +323,7 @@ const SetupKeyNew = () => {
|
||||
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
style={{marginBottom: "0px", marginTop: "10px"}}
|
||||
style={{marginBottom: "0px", marginTop: "-10px"}}
|
||||
name="name"
|
||||
rules={[{required: true, message: "Please enter key name."}]}
|
||||
>
|
||||
@@ -382,9 +382,9 @@ const SetupKeyNew = () => {
|
||||
>
|
||||
Reusable
|
||||
</Paragraph>
|
||||
<Paragraph type={"secondary"} style={{whiteSpace: "pre-line", margin: 0}}>
|
||||
<Text type={"secondary"}>
|
||||
Use this type to enroll multiple peers
|
||||
</Paragraph>
|
||||
</Text>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Row justify={"end"}>
|
||||
@@ -410,10 +410,10 @@ const SetupKeyNew = () => {
|
||||
<Paragraph style={{whiteSpace: "pre-line", margin: 0, fontWeight: "bold"}}>
|
||||
{formSetupKey.type === "one-off" ? "One-off" : "Reusable"}
|
||||
</Paragraph>
|
||||
<Text type={"secondary"} style={{marginLeft: "5px"}}>key</Text>
|
||||
<Text type={"secondary"} style={{marginLeft: "5px"}}>type</Text>
|
||||
</Row>)}
|
||||
|
||||
<Row style={{marginTop: isEditMode? "20px" : "10px"}}>
|
||||
<Row style={{marginTop: "10px"}}>
|
||||
<Col span={24}>
|
||||
<Paragraph
|
||||
style={{whiteSpace: "pre-line", margin: 0, fontWeight: "bold"}}
|
||||
@@ -421,67 +421,40 @@ const SetupKeyNew = () => {
|
||||
{isEditMode ? "Available uses" : "Usage limit"}
|
||||
</Paragraph>
|
||||
</Col>
|
||||
|
||||
|
||||
{isEditMode && (
|
||||
<Col>
|
||||
<Col>
|
||||
{isEditMode ? (
|
||||
<Input
|
||||
disabled
|
||||
value={formSetupKey.usage_limit - formSetupKey.used_times}
|
||||
value={formSetupKey.usage_limit}
|
||||
suffix={<LockOutlined style={{color: "#BFBFBF"}}/>}
|
||||
style={{width: "104px", marginTop: "5px"}}
|
||||
style={{width: "104px"}}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
|
||||
{!isEditMode && (
|
||||
<Col>
|
||||
) : (
|
||||
<Form.Item
|
||||
style={{marginTop: "5px", width: "112px", marginBottom: "5px"}}
|
||||
name="usage_limit"
|
||||
>
|
||||
<InputNumber
|
||||
type={"number"}
|
||||
style={{marginTop: "5px", width: "112px"}}
|
||||
disabled={setupKey.id || formSetupKey.type !== "reusable"}
|
||||
controls={false}
|
||||
min={0}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Paragraph type={"secondary"} style={{marginTop: "-18px", marginBottom: 0}}>
|
||||
)}
|
||||
</Col>
|
||||
{isEditMode ? (
|
||||
<></>
|
||||
) : (
|
||||
<Col span={24}>
|
||||
<Text type={"secondary"}>
|
||||
For example, set to 30 if you want to enroll 30 peers
|
||||
</Paragraph>
|
||||
|
||||
</Text>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
{!isEditMode && (
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
<Col span={24}>
|
||||
<Paragraph
|
||||
style={{whiteSpace: "pre-line", margin: 0, fontWeight: "bold"}}
|
||||
>
|
||||
Expires in
|
||||
</Paragraph>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item
|
||||
name="expiresIn"
|
||||
rules={[{required: true, message: "Please enter expiration date"}]}
|
||||
>
|
||||
<InputNumber defaultValue={7} placeholder={`2`} type="number" addonAfter=" Days"
|
||||
style={{width: "160px", marginTop: "5px"}}/>
|
||||
</Form.Item>
|
||||
<Paragraph type={"secondary"} style={{marginTop: "-18px", marginBottom: 0}}>
|
||||
Should be between 1 and 180 days
|
||||
</Paragraph>
|
||||
</Col>
|
||||
</Row>)}
|
||||
|
||||
{isEditMode && (
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
|
||||
<Row style={{marginTop: "15px"}}>
|
||||
{isEditMode ? (
|
||||
<Container style={{width: "100%", padding: 0, margin: 0}}>
|
||||
<Row>
|
||||
<Col span={11}>
|
||||
@@ -490,7 +463,6 @@ const SetupKeyNew = () => {
|
||||
</Paragraph>
|
||||
<Row>
|
||||
<Input
|
||||
style={{marginTop: "5px"}}
|
||||
disabled
|
||||
suffix={<LockOutlined style={{color: "#BFBFBF"}}/>}
|
||||
value={customExpiresFormat(new Date(formSetupKey.expires))!}
|
||||
@@ -504,7 +476,6 @@ const SetupKeyNew = () => {
|
||||
<Row>
|
||||
<Input
|
||||
disabled
|
||||
style={{marginTop: "5px"}}
|
||||
suffix={<LockOutlined style={{color: "#BFBFBF"}}/>}
|
||||
value={customLastUsedFormat(new Date(formSetupKey.last_used))!}
|
||||
/>
|
||||
@@ -512,8 +483,62 @@ const SetupKeyNew = () => {
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
) : (
|
||||
<>
|
||||
<Col span={24}>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
Expires in
|
||||
</Paragraph>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item
|
||||
style={{
|
||||
marginTop: "5px",
|
||||
width: "112px",
|
||||
marginBottom: "0px",
|
||||
}}
|
||||
name="expiresIn"
|
||||
rules={[{required: true, message: "Please enter expiration date."}]}
|
||||
>
|
||||
<Col
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
}}
|
||||
>
|
||||
<InputNumber
|
||||
type="number"
|
||||
controls={false}
|
||||
placeholder={`0`}
|
||||
min={0}
|
||||
max={180}
|
||||
/>
|
||||
<Col
|
||||
style={{
|
||||
marginLeft: "7px",
|
||||
padding: "4px 12px",
|
||||
border: "1px solid #D9D9D9",
|
||||
borderRadius: "2px",
|
||||
}}
|
||||
>
|
||||
Days
|
||||
</Col>
|
||||
</Col>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
</Row>)}
|
||||
<Col span={24} style={{marginTop: "5px"}}>
|
||||
<Text type={"secondary"}>Should be between 1 and 180 days</Text>
|
||||
</Col>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
<Col span={24}>
|
||||
@@ -564,8 +589,7 @@ const SetupKeyNew = () => {
|
||||
</Form>
|
||||
</Container>
|
||||
</Modal>
|
||||
)
|
||||
;
|
||||
);
|
||||
};
|
||||
|
||||
export default SetupKeyNew;
|
||||
export default SetupKeyNew;
|
||||
+53
-53
@@ -10,7 +10,7 @@ import {
|
||||
Row,
|
||||
Select,
|
||||
Skeleton,
|
||||
Space, Switch, Table,
|
||||
Space, Table,
|
||||
Tag, Typography
|
||||
} from "antd";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
@@ -63,7 +63,6 @@ const UserEdit = () => {
|
||||
|
||||
const [formUser, setFormUser] = useState({} as FormUser)
|
||||
const [form] = Form.useForm()
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
|
||||
const [confirmModal, confirmModalContextHolder] = Modal.useModal();
|
||||
|
||||
@@ -88,20 +87,10 @@ const UserEdit = () => {
|
||||
name: values.name,
|
||||
groupsToCreate: groupsToCreate,
|
||||
auto_groups: autoGroups,
|
||||
is_service_user: isServiceUser,
|
||||
is_blocked: !values.is_active
|
||||
is_service_user: isServiceUser
|
||||
} as UserToSave
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if(users) {
|
||||
let currentUser = users.find((user) => user.is_current)
|
||||
if(currentUser) {
|
||||
setIsAdmin(currentUser.role === "admin");
|
||||
}
|
||||
}
|
||||
}, [users])
|
||||
|
||||
const handleFormSubmit = () => {
|
||||
form.validateFields()
|
||||
.then((values) => {
|
||||
@@ -113,6 +102,18 @@ const UserEdit = () => {
|
||||
dispatch(userActions.setEditUserPopupVisible(false));
|
||||
dispatch(userActions.setUser(null as unknown as User))
|
||||
dispatch(personalAccessTokenActions.resetPersonalAccessTokens(null))
|
||||
dispatch(groupActions.getGroups.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: null
|
||||
}));
|
||||
dispatch(userActions.getServiceUsers.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: null
|
||||
}))
|
||||
dispatch(userActions.getUsers.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: null
|
||||
}))
|
||||
})
|
||||
.catch((errorInfo) => {
|
||||
console.log('errorInfo', errorInfo)
|
||||
@@ -285,13 +286,29 @@ const UserEdit = () => {
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
email: user.email,
|
||||
is_blocked: user.is_blocked,
|
||||
is_active: !user.is_blocked,
|
||||
autoGroupsNames: currentGroups,
|
||||
})
|
||||
}
|
||||
}, [form, user, currentGroups])
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<Text onClick={() => onBreadcrumbUsersClick("Users")}>
|
||||
Users
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: (
|
||||
<Text onClick={() => onBreadcrumbUsersClick("Service Users")}>
|
||||
Service Users
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const isUserAdmin = (userId: string): boolean => {
|
||||
return users.find(u => u.id === userId)?.role === "admin"
|
||||
@@ -303,10 +320,10 @@ const UserEdit = () => {
|
||||
<Breadcrumb style={{marginBottom: "30px"}}
|
||||
items={[
|
||||
{
|
||||
title: <a onClick={() => onBreadcrumbUsersClick("Users")}>All Users</a>,
|
||||
title: <Text onClick={() => onBreadcrumbUsersClick("Users")}>All Users</Text>,
|
||||
},
|
||||
{
|
||||
title: <a onClick={() => onBreadcrumbUsersClick(tab)}>{tab}</a>,
|
||||
title: <Text onClick={() => onBreadcrumbUsersClick(tab)}>{tab}</Text>,
|
||||
// menu: { items: menuItems },
|
||||
},
|
||||
{
|
||||
@@ -327,8 +344,6 @@ const UserEdit = () => {
|
||||
name: formUser.name,
|
||||
role: formUser.role,
|
||||
email: formUser.email,
|
||||
is_blocked: formUser.is_blocked,
|
||||
is_active: !formUser.is_blocked,
|
||||
autoGroupsNames: formUser.autoGroupsNames,
|
||||
}}
|
||||
>
|
||||
@@ -361,45 +376,30 @@ const UserEdit = () => {
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{!user.is_service_user && <Row style={{paddingBottom: "15px"}}>
|
||||
|
||||
<Col xs={24} sm={24} md={11} lg={11} xl={11} xxl={11} span={11}>
|
||||
<Form.Item
|
||||
name="autoGroupsNames"
|
||||
label={<Text style={{}}>Auto-assigned groups</Text>}
|
||||
tooltip="Every peer enrolled with this user will be automatically added to these groups"
|
||||
rules={[{validator: selectValidator}]}
|
||||
style={{marginRight: "70px"}}
|
||||
>
|
||||
<Select mode="tags"
|
||||
placeholder="Associate groups with the user"
|
||||
tagRender={tagRender}
|
||||
dropdownRender={dropDownRender}
|
||||
disabled={oidcUser && !isUserAdmin(oidcUser.sub)}
|
||||
>
|
||||
{
|
||||
tagGroups.map(m =>
|
||||
<Option key={m}>{optionRender(m)}</Option>
|
||||
)
|
||||
}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
{!user.is_current && isAdmin && (
|
||||
<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
||||
<Col span={9}>
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
name="is_active"
|
||||
label="Active"
|
||||
style={{marginRight: "50px"}}
|
||||
name="autoGroupsNames"
|
||||
label={<Text style={{}}>Auto-assigned groups</Text>}
|
||||
tooltip="Every peer enrolled with this user will be automatically added to these groups"
|
||||
rules={[{validator: selectValidator}]}
|
||||
>
|
||||
<Switch/>
|
||||
<Select mode="tags"
|
||||
style={{width: '100%'}}
|
||||
placeholder="Associate groups with the user"
|
||||
tagRender={tagRender}
|
||||
dropdownRender={dropDownRender}
|
||||
disabled={oidcUser && !isUserAdmin(oidcUser.sub)}
|
||||
>
|
||||
{
|
||||
tagGroups.map(m =>
|
||||
<Option key={m}>{optionRender(m)}</Option>
|
||||
)
|
||||
}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>)}
|
||||
</Col>
|
||||
</Row>}
|
||||
|
||||
<Space style={{display: 'flex', justifyContent: 'start'}}>
|
||||
<Button disabled={loading} onClick={onCancel}>Cancel</Button>
|
||||
<Button type="primary"
|
||||
|
||||
+50
-914
File diff suppressed because it is too large.
Load diff
+12
-2
@@ -5,6 +5,8 @@ import service from './service';
|
||||
import actions from './actions';
|
||||
import serviceGroup from "../group/service";
|
||||
import {Group} from "../group/types";
|
||||
import {actions as groupActions} from "../group";
|
||||
import {PersonalAccessToken} from "../personal-access-token/types";
|
||||
|
||||
export function* getUsers(action: ReturnType<typeof actions.getUsers.request>): Generator {
|
||||
try {
|
||||
@@ -72,8 +74,7 @@ export function* saveUser(action: ReturnType<typeof actions.saveUser.request>):
|
||||
email: userToSave.email,
|
||||
role: userToSave.role,
|
||||
auto_groups: newGroups,
|
||||
is_service_user: userToSave.is_service_user,
|
||||
is_blocked: userToSave.is_blocked
|
||||
is_service_user: userToSave.is_service_user
|
||||
} as UserToSave
|
||||
|
||||
let effect
|
||||
@@ -98,6 +99,15 @@ export function* saveUser(action: ReturnType<typeof actions.saveUser.request>):
|
||||
error: null,
|
||||
data: response.body
|
||||
} as CreateResponse<User | null>));
|
||||
|
||||
yield put(groupActions.getGroups.request({
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: null
|
||||
}));
|
||||
yield put(actions.getUsers.request({
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: null
|
||||
}));
|
||||
} catch (err) {
|
||||
yield put(actions.saveUser.failure({
|
||||
loading: false,
|
||||
|
||||
@@ -7,12 +7,10 @@ export interface User {
|
||||
auto_groups: string[];
|
||||
is_current?: boolean;
|
||||
is_service_user?: boolean;
|
||||
is_blocked?: boolean;
|
||||
}
|
||||
|
||||
export interface FormUser extends User {
|
||||
autoGroupsNames: string[]
|
||||
is_active?: boolean
|
||||
}
|
||||
|
||||
export interface UserToSave extends User {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
:root {
|
||||
--shiki-color-text: theme('colors.white');
|
||||
--shiki-token-constant: theme('colors.orange.300');
|
||||
--shiki-token-string: theme('colors.orange.300');
|
||||
--shiki-token-comment: theme('colors.zinc.500');
|
||||
--shiki-token-keyword: theme('colors.sky.300');
|
||||
--shiki-token-parameter: theme('colors.pink.300');
|
||||
--shiki-token-function: theme('colors.violet.300');
|
||||
--shiki-token-string-expression: theme('colors.orange.300');
|
||||
--shiki-token-punctuation: theme('colors.zinc.200');
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.videowrapper {
|
||||
float: none;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
padding-bottom: 56.25%;
|
||||
padding-top: 25px;
|
||||
height: 0;
|
||||
}
|
||||
.videowrapper iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import {Rule} from "../store/rule/types";
|
||||
import {actions as ruleActions} from "../store/rule";
|
||||
import {actions as groupActions} from "../store/group";
|
||||
import {filter, sortBy} from "lodash";
|
||||
import {CloseOutlined, EllipsisOutlined, ExclamationCircleOutlined} from "@ant-design/icons";
|
||||
import {CloseOutlined, ExclamationCircleOutlined} from "@ant-design/icons";
|
||||
import bidirect from '../assets/direct_bi.svg';
|
||||
import inbound from '../assets/direct_in.svg';
|
||||
import outbound from '../assets/direct_out.svg';
|
||||
@@ -37,7 +37,6 @@ import AccessControlModalGroups from "../components/AccessControlModalGroups";
|
||||
import tableSpin from "../components/Spin";
|
||||
import {useGetTokenSilently} from "../utils/token";
|
||||
import {usePageSizeHelpers} from "../utils/pageSize";
|
||||
import {PeerDataTable} from "../store/peer/types";
|
||||
|
||||
const {Title, Paragraph, Text} = Typography;
|
||||
const {Column} = Table;
|
||||
@@ -430,9 +429,9 @@ export const AccessControl = () => {
|
||||
/>
|
||||
<Column title="Direction" dataIndex="flow"
|
||||
render={(text, record: RuleDataTable, index) => {
|
||||
const s = {minWidth: 50, textAlign: "center", fontSize: "20pt", paddingBottom: "23px"} as React.CSSProperties
|
||||
const s = {minWidth: 50, textAlign: "center"} as React.CSSProperties
|
||||
if (text === "bidirect")
|
||||
return <Tag color="processing" style={s}>↔</Tag>
|
||||
return <Tag color="processing" style={s}><img src={bidirect}/></Tag>
|
||||
else if (text === "srcToDest") {
|
||||
return <Tag color="green" style={s}><img src={outbound}/></Tag>
|
||||
} else if (text === "destToSrc") {
|
||||
@@ -450,17 +449,11 @@ export const AccessControl = () => {
|
||||
<Column title="" align="center"
|
||||
render={(text, record, index) => {
|
||||
if (deletedRule.loading || savedRule.loading) return <></>
|
||||
return (
|
||||
<Dropdown trigger={["click"]} overlay={actionsMenu} onOpenChange={visible => {
|
||||
if (visible) setRuleToAction(record as RuleDataTable)
|
||||
}}>
|
||||
<Button type="text">
|
||||
<Space>
|
||||
<EllipsisOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
)
|
||||
return <Dropdown.Button type="text" overlay={actionsMenu}
|
||||
trigger={["click"]}
|
||||
onOpenChange={visible => {
|
||||
if (visible) setRuleToAction(record as RuleDataTable)
|
||||
}}></Dropdown.Button>
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
@@ -31,7 +31,7 @@ import {Group} from "../store/group/types";
|
||||
import {TooltipPlacement} from "antd/es/tooltip";
|
||||
import {NameServer, NameServerGroup} from "../store/nameservers/types";
|
||||
import NameServerGroupUpdate from "../components/NameServerGroupUpdate";
|
||||
import {EllipsisOutlined, ExclamationCircleOutlined} from "@ant-design/icons";
|
||||
import {ExclamationCircleOutlined} from "@ant-design/icons";
|
||||
import {useGetGroupTagHelpers} from "../utils/groups";
|
||||
import {usePageSizeHelpers} from "../utils/pageSize";
|
||||
|
||||
@@ -441,16 +441,11 @@ export const Nameservers = () => {
|
||||
<Column title="" align="center" width="30px"
|
||||
render={(text, record) => {
|
||||
return (
|
||||
<Dropdown trigger={["click"]} overlay={actionsMenu} onOpenChange={visible => {
|
||||
if (visible) setNsGroupToAction(record as NameserverGroupDataTable)
|
||||
}}>
|
||||
<Button type="text">
|
||||
<Space>
|
||||
<EllipsisOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
)
|
||||
<Dropdown.Button type="text" overlay={actionsMenu}
|
||||
trigger={["click"]}
|
||||
onOpenChange={visible => {
|
||||
if (visible) setNsGroupToAction(record as NameserverGroupDataTable)
|
||||
}}></Dropdown.Button>)
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
+6
-12
@@ -6,7 +6,7 @@ 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 {EllipsisOutlined, ExclamationCircleOutlined, ReloadOutlined,} from '@ant-design/icons';
|
||||
import {ExclamationCircleOutlined, ReloadOutlined,} from '@ant-design/icons';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
@@ -611,17 +611,11 @@ export const Peers = () => {
|
||||
<Column title="Version" dataIndex="version"/>
|
||||
<Column title="" align="center"
|
||||
render={(text, record, index) => {
|
||||
return (
|
||||
<Dropdown trigger={["click"]} overlay={actionsMenu} onOpenChange={visible => {
|
||||
if (visible) setPeerToAction(record as PeerDataTable)
|
||||
}}>
|
||||
<Button type="text">
|
||||
<Space>
|
||||
<EllipsisOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
)
|
||||
return <Dropdown.Button type="text" overlay={actionsMenu}
|
||||
trigger={["click"]}
|
||||
onOpenChange={visible => {
|
||||
if (visible) setPeerToAction(record as PeerDataTable)
|
||||
}}></Dropdown.Button>
|
||||
}}
|
||||
/>
|
||||
</Table>)}
|
||||
|
||||
+41
-106
@@ -1,4 +1,4 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {RootState} from "typesafe-actions";
|
||||
import {actions as userActions} from '../store/user';
|
||||
@@ -8,33 +8,29 @@ import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Dropdown, Empty,
|
||||
Dropdown,
|
||||
Input,
|
||||
Menu,
|
||||
message, Modal,
|
||||
Popover,
|
||||
Row,
|
||||
Select,
|
||||
Space, Switch,
|
||||
Space,
|
||||
Table,
|
||||
Tag, Tooltip,
|
||||
Tag,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import {User, UserToSave} from "../store/user/types";
|
||||
import {User} from "../store/user/types";
|
||||
import {filter} from "lodash";
|
||||
import tableSpin from "../components/Spin";
|
||||
import {useGetTokenSilently} from "../utils/token";
|
||||
import {actions as groupActions} from "../store/group";
|
||||
import {Group} from "../store/group/types";
|
||||
import {TooltipPlacement} from "antd/es/tooltip";
|
||||
import {capitalize, isLocalDev, isNetBirdHosted} from "../utils/common";
|
||||
import {isLocalDev, isNetBirdHosted} from "../utils/common";
|
||||
import {usePageSizeHelpers} from "../utils/pageSize";
|
||||
import AddServiceUserPopup from "../components/popups/AddServiceUserPopup";
|
||||
import InviteUserPopup from "../components/popups/InviteUserPopup";
|
||||
import {Peer, PeerDataTable} from "../store/peer/types";
|
||||
import {ExclamationCircleOutlined, MinusOutlined} from "@ant-design/icons";
|
||||
import {actions as peerActions} from "../store/peer";
|
||||
import {useOidcUser} from "@axa-fr/react-oidc";
|
||||
|
||||
const {Title, Paragraph, Text} = Typography;
|
||||
const {Column} = Table;
|
||||
@@ -46,12 +42,10 @@ interface UserDataTable extends User {
|
||||
const styleNotification = {marginTop: 85}
|
||||
|
||||
export const RegularUsers = () => {
|
||||
const {onChangePageSize, pageSizeOptions, pageSize} = usePageSizeHelpers()
|
||||
const {onChangePageSize,pageSizeOptions,pageSize} = usePageSizeHelpers()
|
||||
const {getTokenSilently} = useGetTokenSilently()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
|
||||
const groups = useSelector((state: RootState) => state.group.data)
|
||||
const users = useSelector((state: RootState) => state.user.regularUsers);
|
||||
const failed = useSelector((state: RootState) => state.user.failed);
|
||||
@@ -120,7 +114,6 @@ export const RegularUsers = () => {
|
||||
auto_groups: userToAction?.auto_groups ? userToAction?.auto_groups : [],
|
||||
name: userToAction?.name,
|
||||
role: userToAction?.role,
|
||||
is_blocked: userToAction?.is_blocked,
|
||||
} as User));
|
||||
}
|
||||
|
||||
@@ -129,15 +122,6 @@ export const RegularUsers = () => {
|
||||
dispatch(userActions.setUser(null as unknown as User));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if(users) {
|
||||
let currentUser = users.find((user) => user.is_current)
|
||||
if(currentUser) {
|
||||
setIsAdmin(currentUser.role === "admin");
|
||||
}
|
||||
}
|
||||
}, [users])
|
||||
|
||||
const renderPopoverGroups = (label: string, rowGroups: string[] | string[] | null, userToAction: UserDataTable) => {
|
||||
|
||||
let groupsMap = new Map<string, Group>();
|
||||
@@ -212,10 +196,10 @@ export const RegularUsers = () => {
|
||||
let errorMsg = "Failed to update user"
|
||||
switch (savedUser.error.statusCode) {
|
||||
case 412:
|
||||
errorMsg = savedUser.error.data
|
||||
break
|
||||
case 403:
|
||||
if (savedUser.error.data) {
|
||||
errorMsg = capitalize(savedUser.error.data.message)
|
||||
}
|
||||
errorMsg = "Failed to update user. You might not have enough permissions."
|
||||
break
|
||||
}
|
||||
message.error({
|
||||
@@ -259,61 +243,18 @@ export const RegularUsers = () => {
|
||||
name: user.name,
|
||||
is_current: user.is_current,
|
||||
is_service_user: user.is_service_user,
|
||||
is_blocked: user.is_blocked
|
||||
} as User));
|
||||
dispatch(userActions.setEditUserPopupVisible(true));
|
||||
}
|
||||
|
||||
const handleBlockUser = (block: boolean, user: UserDataTable) => {
|
||||
if (block) {
|
||||
confirmModal.confirm({
|
||||
icon: <ExclamationCircleOutlined/>,
|
||||
title: "Are you sure you want to deactivate \"" + user.name + "\"?",
|
||||
width: 600,
|
||||
content: <Space direction="vertical" size="small">
|
||||
<Paragraph>Disabling this user will disconnect their devices and block dashboard access.</Paragraph>
|
||||
</Space>,
|
||||
onOk() {
|
||||
let userToSave = createUserToSave(user, block)
|
||||
dispatch(userActions.saveUser.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: userToSave
|
||||
}));
|
||||
},
|
||||
onCancel() {
|
||||
// noop
|
||||
},
|
||||
});
|
||||
} else {
|
||||
let userToSave = createUserToSave(user, block)
|
||||
dispatch(userActions.saveUser.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: userToSave
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const createUserToSave = (values: UserDataTable, block: boolean): UserToSave => {
|
||||
return {
|
||||
id: values.id,
|
||||
role: values.role,
|
||||
name: values.name,
|
||||
groupsToCreate: Array.of(),
|
||||
auto_groups: values.auto_groups,
|
||||
is_service_user: values.is_service_user,
|
||||
is_blocked: block
|
||||
} as UserToSave
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container style={{padding: "0px"}}>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Paragraph>Manage users and their
|
||||
permissions.{(window.location.hostname == "app.netbird.io") ? "Same-domain email users are added automatically on first sign-in." : ""}</Paragraph>
|
||||
<Paragraph>Manage users and their permissions.{(window.location.hostname == "app.netbird.io") ? "Same-domain email users are added automatically on first sign-in." : ""}</Paragraph>
|
||||
<Space direction="vertical" size="large" style={{display: 'flex'}}>
|
||||
<Row gutter={[16, 24]} style={{paddingBottom: "0px"}}>
|
||||
<Row gutter={[16, 24]}>
|
||||
<Col xs={24} sm={24} md={8} lg={8} xl={8} xxl={8} span={8}>
|
||||
<Input allowClear value={textToSearch} onPressEnter={searchDataTable}
|
||||
placeholder="Search..." onChange={onChangeTextToSearch}/>
|
||||
@@ -331,16 +272,15 @@ export const RegularUsers = () => {
|
||||
xl={5}
|
||||
xxl={5} span={5}>
|
||||
{(isNetBirdHosted() || isLocalDev()) &&
|
||||
<Row justify="end">
|
||||
<Col>
|
||||
<Button type="primary" onClick={onClickInviteUser}>Invite user</Button>
|
||||
</Col>
|
||||
</Row>}
|
||||
<Row justify="end">
|
||||
<Col>
|
||||
<Button type="primary" onClick={onClickInviteUser}>Invite User</Button>
|
||||
</Col>
|
||||
</Row>}
|
||||
</Col>
|
||||
</Row>
|
||||
{failed &&
|
||||
<Alert message={failed.message} description={failed.data ? failed.data.message : " "}
|
||||
type="error" showIcon
|
||||
<Alert message={failed.message} description={failed.data ? failed.data.message : " "} type="error" showIcon
|
||||
closable/>
|
||||
}
|
||||
<Card bodyStyle={{padding: 0}}>
|
||||
@@ -365,7 +305,6 @@ export const RegularUsers = () => {
|
||||
className="tooltip-label">
|
||||
<Text
|
||||
strong>{(text && text.trim() !== "") ? text : (record as User).id}</Text>
|
||||
|
||||
</Button>
|
||||
|
||||
if ((record as User).is_current) {
|
||||
@@ -374,18 +313,25 @@ export const RegularUsers = () => {
|
||||
</div>
|
||||
}
|
||||
|
||||
if ((record as User).status === "invited") {
|
||||
return <div>{btn}
|
||||
<Tag color="gold">invited</Tag>
|
||||
</div>
|
||||
}
|
||||
|
||||
return btn
|
||||
}}
|
||||
/>
|
||||
<Column title="Name" dataIndex="name"
|
||||
onFilter={(value: string | number | boolean, record) => (record as any).name.includes(value)}
|
||||
sorter={(a, b) => ((a as any).name.localeCompare((b as any).name))}/>
|
||||
<Column title="Status" dataIndex="status"
|
||||
align="center"
|
||||
onFilter={(value: string | number | boolean, record) => (record as any).status.includes(value)}
|
||||
sorter={(a, b) => ((a as any).status.localeCompare((b as any).status))}
|
||||
render={(text, record, index) => {
|
||||
if (text == "active") {
|
||||
return <Tag color="green">{text}</Tag>
|
||||
} else if (text === "invited"){
|
||||
return <Tag color="gold">{text}</Tag>
|
||||
}
|
||||
return <Tag color="red">{text}</Tag>
|
||||
}}
|
||||
/>
|
||||
<Column title="Groups" dataIndex="groupsCount" align="center"
|
||||
render={(text, record: UserDataTable, index) => {
|
||||
return renderPopoverGroups(text, record.auto_groups, record)
|
||||
@@ -394,27 +340,16 @@ export const RegularUsers = () => {
|
||||
<Column title="Role" dataIndex="role"
|
||||
onFilter={(value: string | number | boolean, record) => (record as any).role.includes(value)}
|
||||
sorter={(a, b) => ((a as any).role.localeCompare((b as any).role))}/>
|
||||
{isAdmin && (
|
||||
<Column title="Active" align="center" width="150px" dataIndex="is_blocked"
|
||||
render={(e, record: UserDataTable, index) => {
|
||||
let witch = <Switch size={"small"} checked={!e}
|
||||
disabled={record.is_current}
|
||||
onClick={(active: boolean) => {
|
||||
handleBlockUser(!active, record)
|
||||
}}
|
||||
/>
|
||||
|
||||
if (record.is_current) {
|
||||
return <Tooltip
|
||||
title="You can't block or unblock yourself">
|
||||
<Empty image={""} description={""} style={{height: "1px", width: "auto"}}/>
|
||||
</Tooltip>
|
||||
}
|
||||
|
||||
return witch
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{/*<Column title="" align="center" width="30px"*/}
|
||||
{/* render={(text, record, index) => {*/}
|
||||
{/* return (*/}
|
||||
{/* <Dropdown.Button type="text" overlay={actionsMenu}*/}
|
||||
{/* trigger={["click"]}*/}
|
||||
{/* onVisibleChange={visible => {*/}
|
||||
{/* if (visible) setUserToAction(record as UserDataTable)*/}
|
||||
{/* }}></Dropdown.Button>)*/}
|
||||
{/* }}*/}
|
||||
{/*/>*/}
|
||||
</Table>
|
||||
</Card>
|
||||
</Space>
|
||||
|
||||
+5
-12
@@ -28,7 +28,7 @@ import {Route, RouteToSave} from "../store/route/types";
|
||||
import {actions as routeActions} from "../store/route";
|
||||
import {actions as peerActions} from "../store/peer";
|
||||
import {filter, sortBy} from "lodash";
|
||||
import {EllipsisOutlined, ExclamationCircleOutlined, QuestionCircleOutlined} from "@ant-design/icons";
|
||||
import {ExclamationCircleOutlined, QuestionCircleOutlined} from "@ant-design/icons";
|
||||
import RouteUpdate from "../components/RouteUpdate";
|
||||
import tableSpin from "../components/Spin";
|
||||
import {
|
||||
@@ -410,17 +410,10 @@ export const Routes = () => {
|
||||
<Column title="" align="center"
|
||||
render={(text, record) => {
|
||||
if (deletedRoute.loading || savedRoute.loading) return <></>
|
||||
return (
|
||||
<Dropdown trigger={["click"]} overlay={actionsMenu} onOpenChange={visible => {
|
||||
if (visible) setRouteToAction(record as RouteDataTable)
|
||||
}}>
|
||||
<Button type="text">
|
||||
<Space>
|
||||
<EllipsisOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
)
|
||||
return <Dropdown.Button type="text" overlay={actionsMenu} trigger={["click"]}
|
||||
onOpenChange={visible => {
|
||||
if (visible) setRouteToAction(record as RouteDataTable)
|
||||
}}></Dropdown.Button>
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
@@ -22,7 +22,7 @@ import {filter} from "lodash";
|
||||
import tableSpin from "../components/Spin";
|
||||
import {useGetTokenSilently} from "../utils/token";
|
||||
import {actions as groupActions} from "../store/group";
|
||||
import {capitalize, isLocalDev, isNetBirdHosted} from "../utils/common";
|
||||
import {isLocalDev, isNetBirdHosted} from "../utils/common";
|
||||
import {usePageSizeHelpers} from "../utils/pageSize";
|
||||
import AddServiceUserPopup from "../components/popups/AddServiceUserPopup";
|
||||
import {ExclamationCircleOutlined} from "@ant-design/icons";
|
||||
@@ -111,10 +111,10 @@ export const ServiceUsers = () => {
|
||||
let errorMsg = "Failed to update user"
|
||||
switch (savedUser.error.statusCode) {
|
||||
case 412:
|
||||
errorMsg = savedUser.error.data
|
||||
break
|
||||
case 403:
|
||||
if (savedUser.error.data) {
|
||||
errorMsg = capitalize(savedUser.error.data.message)
|
||||
}
|
||||
errorMsg = "Failed to update user. You might not have enough permissions."
|
||||
break
|
||||
}
|
||||
message.error({
|
||||
@@ -187,11 +187,12 @@ export const ServiceUsers = () => {
|
||||
lg={5}
|
||||
xl={5}
|
||||
xxl={5} span={5}>
|
||||
{(isNetBirdHosted() || isLocalDev()) &&
|
||||
<Row justify="end">
|
||||
<Col>
|
||||
<Button type="primary" onClick={onClickCreateServiceUser}>Create Service User</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Row>}
|
||||
</Col>
|
||||
</Row>
|
||||
{failed &&
|
||||
|
||||
@@ -329,9 +329,9 @@ export const SetupKeys = () => {
|
||||
<Col span={24}>
|
||||
<Title level={4}>Setup Keys</Title>
|
||||
<Paragraph
|
||||
// style={{
|
||||
// color: dataTable.length ? "black" : "#818183",
|
||||
// }}
|
||||
style={{
|
||||
color: dataTable.length ? "black" : "#818183",
|
||||
}}
|
||||
>
|
||||
A list of all the setup keys in your account including their name, state, type and
|
||||
expiration.
|
||||
@@ -592,4 +592,4 @@ export const SetupKeys = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SetupKeys;
|
||||
export default SetupKeys;
|
||||
Loaded 20 of 23 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user