mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ad6952dae | ||
|
|
7e5595c365 | ||
|
|
12687379cc | ||
|
|
62d80af186 |
No files matched your search
Generated
+33
@@ -12,6 +12,8 @@
|
|||||||
"@axa-fr/react-oidc": "^5.14.0",
|
"@axa-fr/react-oidc": "^5.14.0",
|
||||||
"@headlessui/react": "^1.5.0",
|
"@headlessui/react": "^1.5.0",
|
||||||
"@heroicons/react": "^1.0.4",
|
"@heroicons/react": "^1.0.4",
|
||||||
|
"@stripe/react-stripe-js": "^2.1.2",
|
||||||
|
"@stripe/stripe-js": "^2.1.0",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
@@ -3405,6 +3407,24 @@
|
|||||||
"@sinonjs/commons": "^1.7.0"
|
"@sinonjs/commons": "^1.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@stripe/react-stripe-js": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-2JcKRvOdMD698uGrY0cINLHb6XHnY24L+wlWlw2+TbI2aXlQZ9t8Mc+KssFES+EuNgISbrp7fJR3w/13epfiOw==",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@stripe/stripe-js": "^1.44.1 || ^2.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@stripe/stripe-js": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-AdMHWqIq/s3TLmoyuzdb6l21STKDKNm82cjce1VelyQGinVNjVgSEPH5UDtxENEP7OzgMZH8RTphFMi0fMJGLQ=="
|
||||||
|
},
|
||||||
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
||||||
@@ -19477,6 +19497,19 @@
|
|||||||
"@sinonjs/commons": "^1.7.0"
|
"@sinonjs/commons": "^1.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@stripe/react-stripe-js": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-2JcKRvOdMD698uGrY0cINLHb6XHnY24L+wlWlw2+TbI2aXlQZ9t8Mc+KssFES+EuNgISbrp7fJR3w/13epfiOw==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@stripe/stripe-js": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-AdMHWqIq/s3TLmoyuzdb6l21STKDKNm82cjce1VelyQGinVNjVgSEPH5UDtxENEP7OzgMZH8RTphFMi0fMJGLQ=="
|
||||||
|
},
|
||||||
"@surma/rollup-plugin-off-main-thread": {
|
"@surma/rollup-plugin-off-main-thread": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
||||||
|
|||||||
+4
-2
@@ -7,6 +7,8 @@
|
|||||||
"@axa-fr/react-oidc": "^5.14.0",
|
"@axa-fr/react-oidc": "^5.14.0",
|
||||||
"@headlessui/react": "^1.5.0",
|
"@headlessui/react": "^1.5.0",
|
||||||
"@heroicons/react": "^1.0.4",
|
"@heroicons/react": "^1.0.4",
|
||||||
|
"@stripe/react-stripe-js": "^2.1.2",
|
||||||
|
"@stripe/stripe-js": "^2.1.0",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
@@ -79,7 +81,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react-syntax-highlighter": "^15.5.3",
|
"@playwright/test": "^1.36.2",
|
||||||
"@playwright/test": "^1.36.2"
|
"@types/react-syntax-highlighter": "^15.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Whitespace-only changes.
@@ -53,6 +53,7 @@ const Navbar = () => {
|
|||||||
"/acls",
|
"/acls",
|
||||||
"/routes",
|
"/routes",
|
||||||
"/dns",
|
"/dns",
|
||||||
|
"/users",
|
||||||
"/activity",
|
"/activity",
|
||||||
"/settings",
|
"/settings",
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ const UserEdit = (props: any) => {
|
|||||||
dispatch(userActions.setUser(null as unknown as User));
|
dispatch(userActions.setUser(null as unknown as User));
|
||||||
dispatch(personalAccessTokenActions.resetPersonalAccessTokens(null));
|
dispatch(personalAccessTokenActions.resetPersonalAccessTokens(null));
|
||||||
dispatch(userActions.setUserTabOpen(key));
|
dispatch(userActions.setUserTabOpen(key));
|
||||||
|
dispatch(userActions.setEditUserPopupVisible(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectValidator = (_: RuleObject, value: string[]) => {
|
const selectValidator = (_: RuleObject, value: string[]) => {
|
||||||
@@ -331,7 +332,7 @@ const UserEdit = (props: any) => {
|
|||||||
// menu: { items: menuItems },
|
// menu: { items: menuItems },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: user.name,
|
title: user?.name,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@@ -386,7 +387,7 @@ const UserEdit = (props: any) => {
|
|||||||
style={{ marginRight: "70px", fontWeight: "500" }}
|
style={{ marginRight: "70px", fontWeight: "500" }}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
disabled={user.id}
|
disabled={user?.id}
|
||||||
value={formUser.email}
|
value={formUser.email}
|
||||||
style={{ color: "#8c8c8c" }}
|
style={{ color: "#8c8c8c" }}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
|||||||
@@ -515,4 +515,8 @@ ul.ant-list-items {
|
|||||||
|
|
||||||
.d-none{
|
.d-none{
|
||||||
display: none!important;
|
display: none!important;
|
||||||
|
}
|
||||||
|
.nohover {
|
||||||
|
background: transparent!important;
|
||||||
|
cursor: text;
|
||||||
}
|
}
|
||||||
+204
-135
@@ -1,198 +1,268 @@
|
|||||||
import {all, call, spawn, put, select, takeLatest} from 'redux-saga/effects';
|
import { all, call, spawn, put, select, takeLatest } from "redux-saga/effects";
|
||||||
import {
|
import {
|
||||||
ApiError,
|
ApiError,
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
ChangeResponse,
|
ChangeResponse,
|
||||||
CreateResponse,
|
CreateResponse,
|
||||||
DeleteResponse
|
DeleteResponse,
|
||||||
} from '../../services/api-client/types';
|
} from "../../services/api-client/types";
|
||||||
import { Peer } from './types'
|
import { Peer } from "./types";
|
||||||
import service from './service';
|
import service from "./service";
|
||||||
import actions from './actions';
|
import actions from "./actions";
|
||||||
import {Group, GroupPeer} from "../group/types";
|
import { Group, GroupPeer } from "../group/types";
|
||||||
import serviceGroup from "../group/service";
|
import serviceGroup from "../group/service";
|
||||||
import {actions as groupActions} from "../group";
|
import { actions as groupActions } from "../group";
|
||||||
|
import userService from "../user/service";
|
||||||
|
export function* getPeers(
|
||||||
export function* getPeers(action: ReturnType<typeof actions.getPeers.request>): Generator {
|
action: ReturnType<typeof actions.getPeers.request>
|
||||||
|
): Generator {
|
||||||
try {
|
try {
|
||||||
|
yield put(
|
||||||
|
actions.setDeletePeer({
|
||||||
|
loading: false,
|
||||||
|
success: false,
|
||||||
|
failure: false,
|
||||||
|
error: null,
|
||||||
|
data: null,
|
||||||
|
} as DeleteResponse<string | null>)
|
||||||
|
);
|
||||||
|
|
||||||
yield put(actions.setDeletePeer({
|
const users: any = yield call(userService.getUsers, action.payload);
|
||||||
loading: false,
|
let currentUser = users.body.find((user: any) => user.is_current);
|
||||||
success: false,
|
|
||||||
failure: false,
|
|
||||||
error: null,
|
|
||||||
data: null
|
|
||||||
} as DeleteResponse<string | null>))
|
|
||||||
|
|
||||||
const effect = yield call(service.getPeers, action.payload);
|
const effect = yield call(service.getPeers, action.payload);
|
||||||
const response = effect as ApiResponse<Peer[]>;
|
const response = effect as ApiResponse<Peer[]>;
|
||||||
yield put(actions.getPeers.success(response.body));
|
let peersBody: any;
|
||||||
|
if (currentUser.role === "user") {
|
||||||
|
peersBody = response.body.filter((pe) => {
|
||||||
|
return pe.user_id === currentUser.id;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
peersBody = response.body;
|
||||||
|
}
|
||||||
|
yield put(actions.getPeers.success(peersBody));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
yield put(actions.getPeers.failure(err as ApiError));
|
yield put(actions.getPeers.failure(err as ApiError));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* setDeletePeer(action: ReturnType<typeof actions.setDeletePeer>): Generator {
|
export function* setDeletePeer(
|
||||||
yield put(actions.setDeletePeer(action.payload))
|
action: ReturnType<typeof actions.setDeletePeer>
|
||||||
|
): Generator {
|
||||||
|
yield put(actions.setDeletePeer(action.payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* deletePeer(action: ReturnType<typeof actions.deletedPeer.request>): Generator {
|
export function* deletePeer(
|
||||||
|
action: ReturnType<typeof actions.deletedPeer.request>
|
||||||
|
): Generator {
|
||||||
try {
|
try {
|
||||||
yield call(actions.setDeletePeer,{
|
yield call(actions.setDeletePeer, {
|
||||||
loading: true,
|
loading: true,
|
||||||
success: false,
|
success: false,
|
||||||
failure: false,
|
failure: false,
|
||||||
error: null,
|
error: null,
|
||||||
data: null
|
data: null,
|
||||||
} as DeleteResponse<string | null>)
|
} as DeleteResponse<string | null>);
|
||||||
|
|
||||||
const effect = yield call(service.deletedPeer, action.payload);
|
const effect = yield call(service.deletedPeer, action.payload);
|
||||||
const response = effect as ApiResponse<any>;
|
const response = effect as ApiResponse<any>;
|
||||||
|
|
||||||
yield put(actions.deletedPeer.success({
|
yield put(
|
||||||
loading: false,
|
actions.deletedPeer.success({
|
||||||
success: true,
|
loading: false,
|
||||||
failure: false,
|
success: true,
|
||||||
error: null,
|
failure: false,
|
||||||
data: response.body
|
error: null,
|
||||||
} as DeleteResponse<string | null>));
|
data: response.body,
|
||||||
|
} as DeleteResponse<string | null>)
|
||||||
|
);
|
||||||
|
|
||||||
const peers = (yield select(state => state.peer.data)) as Peer[]
|
const peers = (yield select((state) => state.peer.data)) as Peer[];
|
||||||
yield put(actions.getPeers.success(peers.filter((p:Peer) => p.id !== action.payload.payload)))
|
yield put(
|
||||||
|
actions.getPeers.success(
|
||||||
|
peers.filter((p: Peer) => p.id !== action.payload.payload)
|
||||||
|
)
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
yield put(actions.deletedPeer.failure({
|
yield put(
|
||||||
loading: false,
|
actions.deletedPeer.failure({
|
||||||
success: false,
|
loading: false,
|
||||||
failure: false,
|
success: false,
|
||||||
error: err as ApiError,
|
failure: false,
|
||||||
data: null
|
error: err as ApiError,
|
||||||
} as DeleteResponse<string | null>));
|
data: null,
|
||||||
|
} as DeleteResponse<string | null>)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* saveGroups(action: ReturnType<typeof actions.saveGroups.request>): Generator {
|
export function* saveGroups(
|
||||||
|
action: ReturnType<typeof actions.saveGroups.request>
|
||||||
|
): Generator {
|
||||||
try {
|
try {
|
||||||
yield put(actions.setSavedGroups({
|
yield put(
|
||||||
loading: true,
|
actions.setSavedGroups({
|
||||||
success: false,
|
loading: true,
|
||||||
failure: false,
|
success: false,
|
||||||
error: null,
|
failure: false,
|
||||||
data: null
|
error: null,
|
||||||
}))
|
data: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const currentGroups = [...(yield select(state => state.group.data)) as Group[]]
|
const currentGroups = [
|
||||||
|
...((yield select((state) => state.group.data)) as Group[]),
|
||||||
|
];
|
||||||
|
|
||||||
const peerGroupsToSave = action.payload.payload
|
const peerGroupsToSave = action.payload.payload;
|
||||||
|
|
||||||
let groupsToSave = [] as Group[]
|
let groupsToSave = [] as Group[];
|
||||||
let groupsNoId = [] as Group[]
|
let groupsNoId = [] as Group[];
|
||||||
|
|
||||||
groupsToSave = groupsToSave.concat(
|
groupsToSave = groupsToSave.concat(
|
||||||
currentGroups
|
currentGroups
|
||||||
.filter(g => peerGroupsToSave.groupsToRemove.includes(g.id || ''))
|
.filter((g) => peerGroupsToSave.groupsToRemove.includes(g.id || ""))
|
||||||
.map(g => ({
|
.map((g) => ({
|
||||||
id: g.id,
|
id: g.id,
|
||||||
name: g.name,
|
name: g.name,
|
||||||
peers: (g.peers as GroupPeer[]).filter(p => p.id !== peerGroupsToSave.ID).map(p => p.id) as string[]
|
peers: (g.peers as GroupPeer[])
|
||||||
}))
|
.filter((p) => p.id !== peerGroupsToSave.ID)
|
||||||
)
|
.map((p) => p.id) as string[],
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
groupsToSave = groupsToSave.concat(
|
groupsToSave = groupsToSave.concat(
|
||||||
currentGroups
|
currentGroups
|
||||||
.filter(g => peerGroupsToSave.groupsToAdd.includes(g.id || ''))
|
.filter((g) => peerGroupsToSave.groupsToAdd.includes(g.id || ""))
|
||||||
.map(g => ({
|
.map((g) => ({
|
||||||
id: g.id,
|
id: g.id,
|
||||||
name: g.name,
|
name: g.name,
|
||||||
Peers: g.peers ? [...(g.peers as GroupPeer[]).map((p:GroupPeer) => p.id), peerGroupsToSave.ID] : [peerGroupsToSave.ID]
|
Peers: g.peers
|
||||||
}))
|
? [
|
||||||
)
|
...(g.peers as GroupPeer[]).map((p: GroupPeer) => p.id),
|
||||||
|
peerGroupsToSave.ID,
|
||||||
|
]
|
||||||
|
: [peerGroupsToSave.ID],
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
groupsNoId = peerGroupsToSave.groupsNoId.map(g => ({
|
groupsNoId = peerGroupsToSave.groupsNoId.map((g) => ({
|
||||||
name: g,
|
name: g,
|
||||||
peers: [peerGroupsToSave.ID]
|
peers: [peerGroupsToSave.ID],
|
||||||
}))
|
}));
|
||||||
|
|
||||||
if (!groupsNoId.length && !groupsToSave.length) {
|
if (!groupsNoId.length && !groupsToSave.length) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responsesGroup = yield all(groupsToSave.map(g => call(serviceGroup.editGroup, {
|
const responsesGroup = yield all(
|
||||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
groupsToSave.map((g) =>
|
||||||
payload: g
|
call(serviceGroup.editGroup, {
|
||||||
})
|
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||||
))
|
payload: g,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const responsesGroupNoId = yield all(groupsNoId.map(g => call(serviceGroup.createGroup, {
|
const responsesGroupNoId = yield all(
|
||||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
groupsNoId.map((g) =>
|
||||||
payload: g
|
call(serviceGroup.createGroup, {
|
||||||
})
|
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||||
))
|
payload: g,
|
||||||
|
})
|
||||||
yield put(actions.saveGroups.success({
|
)
|
||||||
loading: false,
|
);
|
||||||
success: true,
|
|
||||||
failure: false,
|
|
||||||
error: null,
|
|
||||||
data: [...(responsesGroup as ApiResponse<Group>[]).map(r => r.body), ...(responsesGroupNoId as ApiResponse<Group>[]).map(r => r.body)]
|
|
||||||
} as CreateResponse<Group[] | null>))
|
|
||||||
|
|
||||||
yield put(groupActions.getGroups.request({ getAccessTokenSilently: action.payload.getAccessTokenSilently, payload: null }));
|
yield put(
|
||||||
yield put(actions.getPeers.request({ getAccessTokenSilently: action.payload.getAccessTokenSilently, payload: null }));
|
actions.saveGroups.success({
|
||||||
|
loading: false,
|
||||||
|
success: true,
|
||||||
|
failure: false,
|
||||||
|
error: null,
|
||||||
|
data: [
|
||||||
|
...(responsesGroup as ApiResponse<Group>[]).map((r) => r.body),
|
||||||
|
...(responsesGroupNoId as ApiResponse<Group>[]).map((r) => r.body),
|
||||||
|
],
|
||||||
|
} as CreateResponse<Group[] | null>)
|
||||||
|
);
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
groupActions.getGroups.request({
|
||||||
|
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||||
|
payload: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
yield put(
|
||||||
|
actions.getPeers.request({
|
||||||
|
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||||
|
payload: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err);
|
||||||
yield put(actions.saveGroups.failure({
|
yield put(
|
||||||
loading: false,
|
actions.saveGroups.failure({
|
||||||
success: false,
|
loading: false,
|
||||||
failure: true,
|
success: false,
|
||||||
error: err as ApiError,
|
failure: true,
|
||||||
data: null
|
error: err as ApiError,
|
||||||
} as ChangeResponse<Group[] | null>));
|
data: null,
|
||||||
|
} as ChangeResponse<Group[] | null>)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* updatePeer(action: ReturnType<typeof actions.updatePeer.request>): Generator {
|
export function* updatePeer(
|
||||||
|
action: ReturnType<typeof actions.updatePeer.request>
|
||||||
|
): Generator {
|
||||||
try {
|
try {
|
||||||
yield put(actions.setUpdatedPeer({
|
yield put(
|
||||||
loading: true,
|
actions.setUpdatedPeer({
|
||||||
success: false,
|
loading: true,
|
||||||
failure: false,
|
success: false,
|
||||||
error: null,
|
failure: false,
|
||||||
data: null
|
error: null,
|
||||||
}))
|
data: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const peer = action.payload.payload
|
const peer = action.payload.payload;
|
||||||
const peerId = peer.id
|
const peerId = peer.id;
|
||||||
|
|
||||||
const payloadToSave = {
|
const payloadToSave = {
|
||||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||||
payload: peer
|
payload: peer,
|
||||||
}
|
};
|
||||||
|
|
||||||
const effect = yield call(service.updatePeer, payloadToSave)
|
const effect = yield call(service.updatePeer, payloadToSave);
|
||||||
const response = effect as ApiResponse<Peer>;
|
const response = effect as ApiResponse<Peer>;
|
||||||
|
|
||||||
yield put(actions.updatePeer.success({
|
yield put(
|
||||||
loading: false,
|
actions.updatePeer.success({
|
||||||
success: true,
|
loading: false,
|
||||||
failure: false,
|
success: true,
|
||||||
error: null,
|
failure: false,
|
||||||
data: response.body
|
error: null,
|
||||||
} as ChangeResponse<Peer | null>));
|
data: response.body,
|
||||||
|
} as ChangeResponse<Peer | null>)
|
||||||
const peers = (yield select(state => state.peer.data)) as Peer[]
|
);
|
||||||
yield put(actions.getPeers.success(peers.filter((p:Peer) => p.id !== peerId).concat(response.body)))
|
|
||||||
|
|
||||||
|
const peers = (yield select((state) => state.peer.data)) as Peer[];
|
||||||
|
yield put(
|
||||||
|
actions.getPeers.success(
|
||||||
|
peers.filter((p: Peer) => p.id !== peerId).concat(response.body)
|
||||||
|
)
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err);
|
||||||
yield put(actions.updatePeer.failure({
|
yield put(
|
||||||
loading: false,
|
actions.updatePeer.failure({
|
||||||
success: false,
|
loading: false,
|
||||||
failure: true,
|
success: false,
|
||||||
error: err as ApiError,
|
failure: true,
|
||||||
data: null
|
error: err as ApiError,
|
||||||
} as ChangeResponse<Peer | null>));
|
data: null,
|
||||||
|
} as ChangeResponse<Peer | null>)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +271,6 @@ export default function* sagas(): Generator {
|
|||||||
takeLatest(actions.getPeers.request, getPeers),
|
takeLatest(actions.getPeers.request, getPeers),
|
||||||
takeLatest(actions.deletedPeer.request, deletePeer),
|
takeLatest(actions.deletedPeer.request, deletePeer),
|
||||||
takeLatest(actions.saveGroups.request, saveGroups),
|
takeLatest(actions.saveGroups.request, saveGroups),
|
||||||
takeLatest(actions.updatePeer.request, updatePeer)
|
takeLatest(actions.updatePeer.request, updatePeer),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
+76
-67
@@ -434,6 +434,7 @@ export const Peers = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setUpdateGroupsVisible = (peerToAction: Peer, status: boolean) => {
|
const setUpdateGroupsVisible = (peerToAction: Peer, status: boolean) => {
|
||||||
|
if (!isAdmin) return;
|
||||||
if (status) {
|
if (status) {
|
||||||
dispatch(peerActions.setPeer({ ...peerToAction }));
|
dispatch(peerActions.setPeer({ ...peerToAction }));
|
||||||
dispatch(peerActions.setUpdateGroupsVisible(true));
|
dispatch(peerActions.setUpdateGroupsVisible(true));
|
||||||
@@ -502,10 +503,7 @@ export const Peers = () => {
|
|||||||
open={groupPopupVisible === peerToAction.key}
|
open={groupPopupVisible === peerToAction.key}
|
||||||
title={null}
|
title={null}
|
||||||
>
|
>
|
||||||
<Button
|
<Button type="link" onClick={() => setGroupVisible(peerToAction, true)}>
|
||||||
type="link"
|
|
||||||
onClick={() => setGroupVisible(peerToAction, true)}
|
|
||||||
>
|
|
||||||
{label}
|
{label}
|
||||||
</Button>
|
</Button>
|
||||||
</Popover>
|
</Popover>
|
||||||
@@ -592,6 +590,7 @@ export const Peers = () => {
|
|||||||
type="text"
|
type="text"
|
||||||
style={{ height: "auto", whiteSpace: "normal", textAlign: "left" }}
|
style={{ height: "auto", whiteSpace: "normal", textAlign: "left" }}
|
||||||
onClick={() => setUpdateGroupsVisible(peer, true)}
|
onClick={() => setUpdateGroupsVisible(peer, true)}
|
||||||
|
className={!isAdmin ? "nohover" : ""}
|
||||||
>
|
>
|
||||||
<span style={{ textAlign: "left" }}>
|
<span style={{ textAlign: "left" }}>
|
||||||
<Row>
|
<Row>
|
||||||
@@ -607,6 +606,7 @@ export const Peers = () => {
|
|||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
|
className={!isAdmin ? "nohover" : ""}
|
||||||
style={{ height: "auto", textAlign: "left" }}
|
style={{ height: "auto", textAlign: "left" }}
|
||||||
onClick={() => setUpdateGroupsVisible(peer, true)}
|
onClick={() => setUpdateGroupsVisible(peer, true)}
|
||||||
>
|
>
|
||||||
@@ -690,22 +690,28 @@ export const Peers = () => {
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
<Select
|
{isAdmin && (
|
||||||
mode="tags"
|
<Select
|
||||||
placeholder="Filter by groups"
|
mode="tags"
|
||||||
tagRender={blueTagRender}
|
placeholder="Filter by groups"
|
||||||
// dropdownRender={dropDownRender}
|
tagRender={blueTagRender}
|
||||||
optionFilterProp="serchValue"
|
// dropdownRender={dropDownRender}
|
||||||
className="groupsSelect"
|
optionFilterProp="serchValue"
|
||||||
onChange={handleGroupChange}
|
className="groupsSelect"
|
||||||
value={groupsFilterArray}
|
onChange={handleGroupChange}
|
||||||
>
|
value={groupsFilterArray}
|
||||||
{tagGroups.map((m, index) => (
|
>
|
||||||
<Option key={index} value={m.id} serchValue={m.name}>
|
{tagGroups.map((m, index) => (
|
||||||
{optionRender(m.name, m.id)}
|
<Option
|
||||||
</Option>
|
key={index}
|
||||||
))}
|
value={m.id}
|
||||||
</Select>
|
serchValue={m.name}
|
||||||
|
>
|
||||||
|
{optionRender(m.name, m.id)}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
||||||
<Row justify="end">
|
<Row justify="end">
|
||||||
@@ -757,7 +763,7 @@ export const Peers = () => {
|
|||||||
Get started by adding one to your network.
|
Get started by adding one to your network.
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
<Button
|
<Button
|
||||||
data-testid="add-new-peer-button"
|
data-testid="add-new-peer-button"
|
||||||
size={"middle"}
|
size={"middle"}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => setAddPeerModalOpen(true)}
|
onClick={() => setAddPeerModalOpen(true)}
|
||||||
@@ -822,53 +828,56 @@ export const Peers = () => {
|
|||||||
return renderAddress(record);
|
return renderAddress(record);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Column
|
{isAdmin && (
|
||||||
title="Groups"
|
<>
|
||||||
dataIndex="groupsCount"
|
<Column
|
||||||
align="center"
|
title="Groups"
|
||||||
render={(text, record: PeerDataTable, index) => {
|
dataIndex="groupsCount"
|
||||||
return renderPopoverGroups(
|
align="center"
|
||||||
text,
|
render={(text, record: PeerDataTable, index) => {
|
||||||
record.groups,
|
return renderPopoverGroups(
|
||||||
record
|
text,
|
||||||
);
|
record.groups,
|
||||||
}}
|
record
|
||||||
/>
|
);
|
||||||
<Column
|
}}
|
||||||
title="SSH Server"
|
/>
|
||||||
dataIndex="ssh_enabled"
|
<Column
|
||||||
align="center"
|
title="SSH Server"
|
||||||
render={(e, record: PeerDataTable, index) => {
|
dataIndex="ssh_enabled"
|
||||||
let isWindows = record.os
|
align="center"
|
||||||
.toLocaleLowerCase()
|
render={(e, record: PeerDataTable, index) => {
|
||||||
.startsWith("windows");
|
let isWindows = record.os
|
||||||
let toggle = (
|
.toLocaleLowerCase()
|
||||||
<Switch
|
.startsWith("windows");
|
||||||
size={"small"}
|
let toggle = (
|
||||||
checked={e}
|
<Switch
|
||||||
disabled={isWindows}
|
size={"small"}
|
||||||
onClick={(checked: boolean) => {
|
checked={e}
|
||||||
if (checked) {
|
disabled={isWindows}
|
||||||
showConfirmEnableSSH(record);
|
onClick={(checked: boolean) => {
|
||||||
} else {
|
if (checked) {
|
||||||
handleSwitchSSH(record, checked);
|
showConfirmEnableSSH(record);
|
||||||
}
|
} else {
|
||||||
}}
|
handleSwitchSSH(record, checked);
|
||||||
/>
|
}
|
||||||
);
|
}}
|
||||||
|
/>
|
||||||
if (isWindows) {
|
);
|
||||||
return (
|
|
||||||
<Tooltip title="SSH server feature is not yet supported on Windows">
|
|
||||||
{toggle}
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return toggle;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
if (isWindows) {
|
||||||
|
return (
|
||||||
|
<Tooltip title="SSH server feature is not yet supported on Windows">
|
||||||
|
{toggle}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return toggle;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Column
|
<Column
|
||||||
title="LastSeen"
|
title="LastSeen"
|
||||||
dataIndex="last_seen"
|
dataIndex="last_seen"
|
||||||
|
|||||||
+69
-1
@@ -33,6 +33,7 @@ import ExpiresInInput, {
|
|||||||
expiresInToSeconds,
|
expiresInToSeconds,
|
||||||
secondsToExpiresIn,
|
secondsToExpiresIn,
|
||||||
} from "./ExpiresInInput";
|
} from "./ExpiresInInput";
|
||||||
|
// import CheckoutPage from "../components/CheckoutForm";
|
||||||
import Column from "antd/lib/table/Column";
|
import Column from "antd/lib/table/Column";
|
||||||
import TableSpin from "../components/Spin";
|
import TableSpin from "../components/Spin";
|
||||||
import { checkExpiresIn } from "../utils/common";
|
import { checkExpiresIn } from "../utils/common";
|
||||||
@@ -55,6 +56,8 @@ const { Title, Paragraph, Text } = Typography;
|
|||||||
const styleNotification = { marginTop: 85 };
|
const styleNotification = { marginTop: 85 };
|
||||||
|
|
||||||
export const Settings = () => {
|
export const Settings = () => {
|
||||||
|
const [paymentCompleted, setPaymentCompleted] = useState(false);
|
||||||
|
|
||||||
const { getTokenSilently } = useGetTokenSilently();
|
const { getTokenSilently } = useGetTokenSilently();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { pageSize, onChangePageSize, pageSizeOptions } = usePageSizeHelpers(
|
const { pageSize, onChangePageSize, pageSizeOptions } = usePageSizeHelpers(
|
||||||
@@ -571,7 +574,11 @@ export const Settings = () => {
|
|||||||
"System settings",
|
"System settings",
|
||||||
"sub2",
|
"sub2",
|
||||||
<SettingOutlined />,
|
<SettingOutlined />,
|
||||||
[getItem("Authentication", "auth"), getItem("Groups", "groups")],
|
[
|
||||||
|
getItem("Authentication", "auth"),
|
||||||
|
getItem("Groups", "groups"),
|
||||||
|
getItem("Billing", "billing"),
|
||||||
|
],
|
||||||
"group"
|
"group"
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@@ -1122,6 +1129,67 @@ export const Settings = () => {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
|
{billingClicked && (
|
||||||
|
<Row>
|
||||||
|
<Col span={24}>
|
||||||
|
<Space
|
||||||
|
direction="vertical"
|
||||||
|
size="large"
|
||||||
|
style={{ display: "flex" }}
|
||||||
|
>
|
||||||
|
<Row style={{ width: "100%" }}>
|
||||||
|
<Col span={24}>
|
||||||
|
<Card loading={loading}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
color: "rgba(0, 0, 0, 0.88)",
|
||||||
|
fontWeight: "500",
|
||||||
|
fontSize: "18px",
|
||||||
|
marginBottom: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Billing - Free plan
|
||||||
|
</div>
|
||||||
|
<Row>
|
||||||
|
<Col span={12} style={{ marginBottom: "20px" }}>
|
||||||
|
<Alert
|
||||||
|
description="The Free plan allows you to add up to 5 users and 100 peers."
|
||||||
|
type="info"
|
||||||
|
action={
|
||||||
|
<Space>
|
||||||
|
<Button size="small" type="primary">
|
||||||
|
Upgrade
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Space size={30} wrap></Space>
|
||||||
|
<Col span={24}>
|
||||||
|
<Progress
|
||||||
|
strokeColor={"#1890FF"}
|
||||||
|
percent={60}
|
||||||
|
size={[200, 20]}
|
||||||
|
format={(percent) => `${users.length}/5 users`}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<Progress
|
||||||
|
strokeColor={"#1890FF"}
|
||||||
|
percent={10}
|
||||||
|
size={[500, 20]}
|
||||||
|
format={(percent) => `10/100 peers`}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
{/* <CheckoutPage /> */}
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Space>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
Reference in new issue
Block a user