mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e8484cd45 | ||
|
|
6c87f53195 | ||
|
|
9bbbff7dc0 | ||
|
|
04a20fa31f | ||
|
|
3797db93f0 | ||
|
|
2e81765e85 | ||
|
|
cb9f76c0fc | ||
|
|
54accb665c |
No files matched your search
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -284,7 +284,7 @@ const AccessControlNew = () => {
|
||||
<Divider style={{ margin: "8px 0" }} />
|
||||
<Row style={{ padding: "0 8px 4px" }}>
|
||||
<Col flex="auto">
|
||||
<Text type={"secondary"}>Add new ports by pressing "Enter"</Text>
|
||||
<Text type={"secondary"}>Add new groups by pressing "Enter"</Text>
|
||||
</Col>
|
||||
<Col flex="none">
|
||||
<svg
|
||||
|
||||
@@ -179,6 +179,13 @@ const PeerUpdate = (props: any) => {
|
||||
|
||||
useEffect(() => {}, [users]);
|
||||
|
||||
const routeAddAllowed = (os: string): boolean => {
|
||||
return os !== ""
|
||||
&& !os.toLowerCase().startsWith("darwin")
|
||||
&& !os.toLowerCase().startsWith("windows")
|
||||
&& !os.toLowerCase().startsWith("android")
|
||||
}
|
||||
|
||||
const toggleEditName = (status: boolean, value?: string) => {
|
||||
setEditName(status);
|
||||
|
||||
@@ -965,6 +972,7 @@ const PeerUpdate = (props: any) => {
|
||||
{/* --- */}
|
||||
{!isGroupUpdateView && (
|
||||
<>
|
||||
{routeAddAllowed(peer.os) &&
|
||||
<Card
|
||||
bordered={true}
|
||||
// loading={loading}Ć’
|
||||
@@ -1090,7 +1098,7 @@ const PeerUpdate = (props: any) => {
|
||||
</Space>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Card>}
|
||||
|
||||
<Card bordered={true} style={{ marginBottom: "50px" }}>
|
||||
<Col span={24}>
|
||||
|
||||
+101
-98
@@ -13,6 +13,7 @@ import {
|
||||
Tag,
|
||||
Typography,
|
||||
Card,
|
||||
Tooltip,
|
||||
} from "antd";
|
||||
import { RootState } from "typesafe-actions";
|
||||
import {
|
||||
@@ -54,7 +55,7 @@ const SetupKeyNew = (props: any) => {
|
||||
const savedSetupKey = useSelector(
|
||||
(state: RootState) => state.setupKey.savedSetupKey
|
||||
);
|
||||
|
||||
|
||||
const groups = useSelector((state: RootState) => state.group.data);
|
||||
|
||||
const [form] = Form.useForm();
|
||||
@@ -91,14 +92,13 @@ const SetupKeyNew = (props: any) => {
|
||||
.filter((g) => allGroups.get(g))
|
||||
.map((g) => allGroups.get(g)!.name);
|
||||
}
|
||||
|
||||
const fSetupKey = {
|
||||
...setupKey,
|
||||
autoGroupNames: setupKey.auto_groups || [],
|
||||
exp: moment(setupKey.expires),
|
||||
last: moment(setupKey.last_used),
|
||||
} as FormSetupKey;
|
||||
form.setFieldsValue(fSetupKey);
|
||||
form.setFieldsValue(fSetupKey);
|
||||
setFormSetupKey(fSetupKey);
|
||||
}, [setupKey]);
|
||||
|
||||
@@ -126,8 +126,7 @@ const SetupKeyNew = (props: any) => {
|
||||
await form.validateFields();
|
||||
} catch (e) {
|
||||
const errorFields = (e as any).errorFields;
|
||||
return console.log("errorInfo", errorFields);
|
||||
}
|
||||
}
|
||||
|
||||
const setupKeyToSave = createSetupKeyToSave();
|
||||
dispatch(
|
||||
@@ -258,8 +257,7 @@ const SetupKeyNew = (props: any) => {
|
||||
dispatch(personalAccessTokenActions.resetPersonalAccessTokens(null));
|
||||
setVisibleNewSetupKey(false);
|
||||
};
|
||||
|
||||
return (
|
||||
return (
|
||||
<>
|
||||
{!isGroupUpdateView && (
|
||||
<Breadcrumb
|
||||
@@ -303,13 +301,9 @@ const SetupKeyNew = (props: any) => {
|
||||
{!isGroupUpdateView && (
|
||||
<Row style={{ marginTop: "10px" }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={11}
|
||||
lg={11}
|
||||
xl={11}
|
||||
xxl={11}
|
||||
span={11}
|
||||
md={8}
|
||||
lg={8}
|
||||
style={{
|
||||
paddingRight: "70px",
|
||||
}}
|
||||
@@ -343,7 +337,14 @@ const SetupKeyNew = (props: any) => {
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
||||
<Col
|
||||
sm={24}
|
||||
md={8}
|
||||
lg={6}
|
||||
style={{
|
||||
paddingRight: "70px",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
@@ -358,8 +359,48 @@ const SetupKeyNew = (props: any) => {
|
||||
fontWeight: "500",
|
||||
}}
|
||||
></Paragraph>
|
||||
{formSetupKey.type === "one-off" ? "One-off" : "Reusable"},
|
||||
available uses
|
||||
Type{" "}
|
||||
{formSetupKey.ephemeral ? (
|
||||
<Tooltip title="Peers that are offline for over 10 minutes will be removed automatically">
|
||||
<Tag>
|
||||
<Text type="secondary" style={{ fontSize: 10 }}>
|
||||
Ephemeral
|
||||
</Text>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
) : (
|
||||
" "
|
||||
)}
|
||||
</Paragraph>
|
||||
<Col>
|
||||
<Input
|
||||
disabled
|
||||
value={
|
||||
formSetupKey.type === "one-off" ? "One-off" : "Reusable"
|
||||
}
|
||||
suffix={<LockOutlined style={{ color: "#BFBFBF" }} />}
|
||||
style={{ marginTop: "8px" }}
|
||||
/>
|
||||
</Col>
|
||||
</Col>
|
||||
|
||||
<Col sm={24} md={8} lg={3}>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
></Paragraph>
|
||||
{/* {formSetupKey.type === "one-off" ? "One-off" : "Reusable"}, */}
|
||||
Available uses
|
||||
</Paragraph>
|
||||
<Col>
|
||||
<Input
|
||||
@@ -378,44 +419,6 @@ const SetupKeyNew = (props: any) => {
|
||||
</Row>
|
||||
)}
|
||||
<Row style={{ marginTop: `${isGroupUpdateView ? "0" : "39px"}` }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={11}
|
||||
lg={11}
|
||||
xl={11}
|
||||
xxl={11}
|
||||
span={11}
|
||||
style={{
|
||||
paddingRight: "70px",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
></Paragraph>
|
||||
Ephemeral peers enabled?
|
||||
</Paragraph>
|
||||
<Col>
|
||||
<Input
|
||||
disabled
|
||||
value={formSetupKey.ephemeral? "yes" : "no"}
|
||||
suffix={<LockOutlined style={{ color: "#BFBFBF" }} />}
|
||||
style={{ marginTop: "8px" }}
|
||||
/>
|
||||
|
||||
</Col>
|
||||
</Col>
|
||||
{!isGroupUpdateView && (
|
||||
<Col xs={24} sm={24} md={5} lg={5} xl={5} xxl={5} span={5}>
|
||||
<Paragraph style={{ margin: 0, fontWeight: "500" }}>
|
||||
@@ -434,54 +437,54 @@ const SetupKeyNew = (props: any) => {
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
<Row style={{ marginTop: `${isGroupUpdateView ? "0" : "39px"}` }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={!isGroupUpdateView ? 11 : 24}
|
||||
lg={!isGroupUpdateView ? 11 : 24}
|
||||
xl={!isGroupUpdateView ? 11 : 24}
|
||||
xxl={!isGroupUpdateView ? 11 : 24}
|
||||
span={!isGroupUpdateView ? 11 : 24}
|
||||
style={{
|
||||
paddingRight: `${!isGroupUpdateView ? "70px" : "0"}`,
|
||||
}}
|
||||
<Row style={{ marginTop: `${isGroupUpdateView ? "0" : "39px"}` }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={24}
|
||||
md={!isGroupUpdateView ? 11 : 24}
|
||||
lg={!isGroupUpdateView ? 11 : 24}
|
||||
xl={!isGroupUpdateView ? 11 : 24}
|
||||
xxl={!isGroupUpdateView ? 11 : 24}
|
||||
span={!isGroupUpdateView ? 11 : 24}
|
||||
style={{
|
||||
paddingRight: `${!isGroupUpdateView ? "70px" : "0"}`,
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
style={{
|
||||
whiteSpace: "pre-line",
|
||||
margin: 0,
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
Auto-assigned groups
|
||||
</Paragraph>
|
||||
Auto-assigned groups
|
||||
</Paragraph>
|
||||
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
style={{ marginTop: "8px", marginBottom: 0 }}
|
||||
name="autoGroupNames"
|
||||
rules={[{ validator: selectValidator }]}
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
style={{ marginTop: "8px", marginBottom: 0 }}
|
||||
name="autoGroupNames"
|
||||
rules={[{ validator: selectValidator }]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Associate groups with the key"
|
||||
tagRender={blueTagRender}
|
||||
dropdownRender={dropDownRender}
|
||||
// enabled only when we have a new key !setupkey.id or when the key is valid
|
||||
disabled={!(!setupKey.id || setupKey.valid)}
|
||||
optionFilterProp="searchValue"
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Associate groups with the key"
|
||||
tagRender={blueTagRender}
|
||||
dropdownRender={dropDownRender}
|
||||
// enabled only when we have a new key !setupkey.id or when the key is valid
|
||||
disabled={!(!setupKey.id || setupKey.valid)}
|
||||
optionFilterProp="searchValue"
|
||||
>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
{tagGroups.map((m, index) => (
|
||||
<Option key={index} value={m.id} serchValue={m.name}>
|
||||
{optionRender(m.name, m.id)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
@@ -505,7 +505,8 @@ const SetupKeyNew = () => {
|
||||
type={"secondary"}
|
||||
style={{ whiteSpace: "pre-line", margin: 0 }}
|
||||
>
|
||||
Use this type of key to add peers that will be auto removed after being inactive
|
||||
Peers that are offline for over 10 minutes will be removed
|
||||
automatically
|
||||
</Paragraph>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
|
||||
@@ -17,14 +17,16 @@ export const UbuntuTab = () => {
|
||||
key: 1,
|
||||
title: "Add repository",
|
||||
commands: [
|
||||
`sudo apt-get update`,
|
||||
`sudo apt install ca-certificates curl gnupg -y`,
|
||||
`curl -L https://pkgs.wiretrustee.com/debian/public.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/wiretrustee.gpg`,
|
||||
`echo 'deb https://pkgs.wiretrustee.com/debian stable main' | sudo tee /etc/apt/sources.list.d/wiretrustee.list`,
|
||||
`curl -sSL https://pkgs.netbird.io/debian/public.key | sudo gpg --dearmor --output /usr/share/keyrings/netbird-archive-keyring.gpg`,
|
||||
`echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' | sudo tee /etc/apt/sources.list.d/netbird.list`,
|
||||
].join("\n"),
|
||||
commandsForCopy: [
|
||||
`sudo apt install ca-certificates curl gnupg -y`,
|
||||
`curl -L https://pkgs.wiretrustee.com/debian/public.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/wiretrustee.gpg`,
|
||||
`echo 'deb https://pkgs.wiretrustee.com/debian stable main' | sudo tee /etc/apt/sources.list.d/wiretrustee.list`,
|
||||
`sudo apt-get update`,
|
||||
`sudo apt-get install ca-certificates curl gnupg -y`,
|
||||
`curl -sSL https://pkgs.netbird.io/debian/public.key | sudo gpg --dearmor --output /usr/share/keyrings/netbird-archive-keyring.gpg`,
|
||||
`echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' | sudo tee /etc/apt/sources.list.d/netbird.list`,
|
||||
].join("\n"),
|
||||
copied: false,
|
||||
showCopyButton: false,
|
||||
|
||||
@@ -519,4 +519,13 @@ ul.ant-list-items {
|
||||
.nohover {
|
||||
background: transparent!important;
|
||||
cursor: text;
|
||||
}
|
||||
.emp-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.emp-wrapper p {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
import {ExpiresInValue} from "../../views/ExpiresInInput";
|
||||
|
||||
export interface Account {
|
||||
id: string;
|
||||
settings: { peer_login_expiration_enabled: boolean, peer_login_expiration: number}
|
||||
id: string;
|
||||
settings: {
|
||||
peer_login_expiration_enabled: boolean;
|
||||
peer_login_expiration: number;
|
||||
jwt_groups_enabled: boolean;
|
||||
groups_propagation_enabled: boolean;
|
||||
jwt_groups_claim_name: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FormAccount extends Account {
|
||||
peer_login_expiration_enabled: boolean
|
||||
peer_login_expiration_formatted : ExpiresInValue
|
||||
peer_login_expiration_enabled: boolean;
|
||||
jwt_groups_enabled: boolean;
|
||||
groups_propagation_enabled: boolean;
|
||||
jwt_groups_claim_name: string;
|
||||
peer_login_expiration_formatted: ExpiresInValue;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
export interface Event {
|
||||
id: string;
|
||||
timestamp: string
|
||||
activity: string
|
||||
activity_code: string
|
||||
initiator_id: string
|
||||
target_id: string
|
||||
meta: { [key: string]: string }
|
||||
id: string;
|
||||
timestamp: string;
|
||||
activity: string;
|
||||
activity_code: string;
|
||||
initiator_id: string;
|
||||
initiator_email: string;
|
||||
initiator_name: string;
|
||||
target_id: string;
|
||||
meta: { [key: string]: string };
|
||||
}
|
||||
+48
-30
@@ -1,54 +1,72 @@
|
||||
import {ActionType, createAction, createAsyncAction} from 'typesafe-actions';
|
||||
import {User, UserToSave} from './types';
|
||||
import {ApiError, CreateResponse, DeleteResponse, RequestPayload} from '../../services/api-client/types';
|
||||
import { ActionType, createAction, createAsyncAction } from "typesafe-actions";
|
||||
import { User, UserToSave } from "./types";
|
||||
import {
|
||||
ApiError,
|
||||
CreateResponse,
|
||||
DeleteResponse,
|
||||
RequestPayload,
|
||||
} from "../../services/api-client/types";
|
||||
|
||||
const actions = {
|
||||
getUsers: createAsyncAction(
|
||||
'GET_USERS_REQUEST',
|
||||
'GET_USERS_SUCCESS',
|
||||
'GET_USERS_FAILURE',
|
||||
"GET_USERS_REQUEST",
|
||||
"GET_USERS_SUCCESS",
|
||||
"GET_USERS_FAILURE"
|
||||
)<RequestPayload<null>, User[], ApiError>(),
|
||||
|
||||
getServiceUsers: createAsyncAction(
|
||||
'GET_SERVICE_USERS_REQUEST',
|
||||
'GET_SERVICE_USERS_SUCCESS',
|
||||
'GET_SERVICE_USERS_FAILURE',
|
||||
"GET_SERVICE_USERS_REQUEST",
|
||||
"GET_SERVICE_USERS_SUCCESS",
|
||||
"GET_SERVICE_USERS_FAILURE"
|
||||
)<RequestPayload<null>, User[], ApiError>(),
|
||||
|
||||
getRegularUsers: createAsyncAction(
|
||||
'GET_REGULAR_USERS_REQUEST',
|
||||
'GET_REGULAR_USERS_SUCCESS',
|
||||
'GET_REGULAR_USERS_FAILURE',
|
||||
"GET_REGULAR_USERS_REQUEST",
|
||||
"GET_REGULAR_USERS_SUCCESS",
|
||||
"GET_REGULAR_USERS_FAILURE"
|
||||
)<RequestPayload<null>, User[], ApiError>(),
|
||||
|
||||
deleteUser: createAsyncAction(
|
||||
'DELETE_USER_REQUEST',
|
||||
'DELETE_USER_SUCCESS',
|
||||
'DELETE_USER_FAILURE',
|
||||
)<RequestPayload<string>, DeleteResponse<string | null>, DeleteResponse<string | null>>(),
|
||||
setDeletedUser: createAction('SET_DELETED_USER')<DeleteResponse<string | null>>(),
|
||||
resetDeletedUser: createAction('RESET_DELETED_USER')<null>(),
|
||||
"DELETE_USER_REQUEST",
|
||||
"DELETE_USER_SUCCESS",
|
||||
"DELETE_USER_FAILURE"
|
||||
)<
|
||||
RequestPayload<string>,
|
||||
DeleteResponse<string | null>,
|
||||
DeleteResponse<string | null>
|
||||
>(),
|
||||
setDeletedUser:
|
||||
createAction("SET_DELETED_USER")<DeleteResponse<string | null>>(),
|
||||
resetDeletedUser: createAction("RESET_DELETED_USER")<null>(),
|
||||
|
||||
// used to set a user object that was picked in the user table in the UserUpdate drawer (user update window on right-side).
|
||||
setUser: createAction('SET_USER')<User>(),
|
||||
setUser: createAction("SET_USER")<User>(),
|
||||
// used to make the UserUpdate drawer visible in the UI.
|
||||
setUpdateUserDrawerVisible: createAction('SET_UPDATE_USER_VISIBLE')<boolean>(),
|
||||
setUpdateUserDrawerVisible: createAction(
|
||||
"SET_UPDATE_USER_VISIBLE"
|
||||
)<boolean>(),
|
||||
// used to make the ViewUserPopup visible in the UI.
|
||||
setInviteUserPopupVisible: createAction('SET_INVITE_USER_VISIBLE')<boolean>(),
|
||||
setInviteUserPopupVisible: createAction("SET_INVITE_USER_VISIBLE")<boolean>(),
|
||||
// used to make the EditUserPopup visible in the UI.
|
||||
setEditUserPopupVisible: createAction('SET_EDIT_USER_VISIBLE')<boolean>(),
|
||||
setEditUserPopupVisible: createAction("SET_EDIT_USER_VISIBLE")<boolean>(),
|
||||
// used to make the AddServiceUserPopup visible in the UI.
|
||||
setAddServiceUserPopupVisible: createAction('SET_ADD_SERVICE_USER_VISIBLE')<boolean>(),
|
||||
setAddServiceUserPopupVisible: createAction(
|
||||
"SET_ADD_SERVICE_USER_VISIBLE"
|
||||
)<boolean>(),
|
||||
// used to remember what tab was open on users page
|
||||
setUserTabOpen: createAction('SET_USER_TAB_OPEN')<string>(),
|
||||
setUserTabOpen: createAction("SET_USER_TAB_OPEN")<string>(),
|
||||
|
||||
saveUser: createAsyncAction(
|
||||
'SAVE_USER_REQUEST',
|
||||
'SAVE_USER_SUCCESS',
|
||||
'SAVE_USER_FAILURE',
|
||||
)<RequestPayload<UserToSave>, CreateResponse<User | null>, CreateResponse<User | null>>(),
|
||||
setSavedUser: createAction('SET_SAVED_USER')<CreateResponse<User | null>>(),
|
||||
resetSavedUser: createAction('RESET_SAVED_USER')<null>(),
|
||||
"SAVE_USER_REQUEST",
|
||||
"SAVE_USER_SUCCESS",
|
||||
"SAVE_USER_FAILURE"
|
||||
)<
|
||||
RequestPayload<UserToSave>,
|
||||
CreateResponse<User | null>,
|
||||
CreateResponse<User | null>
|
||||
>(),
|
||||
setSavedUser: createAction("SET_SAVED_USER")<CreateResponse<User | null>>(),
|
||||
resetSavedUser: createAction("RESET_SAVED_USER")<null>(),
|
||||
};
|
||||
|
||||
export type ActionTypes = ActionType<typeof actions>;
|
||||
|
||||
+162
-138
@@ -1,159 +1,183 @@
|
||||
import {all, call, put, select, takeLatest} from 'redux-saga/effects';
|
||||
import {ApiError, ApiResponse, CreateResponse, DeleteResponse} from '../../services/api-client/types';
|
||||
import {User, UserToSave} from './types'
|
||||
import service from './service';
|
||||
import actions from './actions';
|
||||
import { all, call, put, select, takeLatest } from "redux-saga/effects";
|
||||
import {
|
||||
ApiError,
|
||||
ApiResponse,
|
||||
CreateResponse,
|
||||
DeleteResponse,
|
||||
} from "../../services/api-client/types";
|
||||
import { User, UserToSave } from "./types";
|
||||
import service from "./service";
|
||||
import actions from "./actions";
|
||||
import serviceGroup from "../group/service";
|
||||
import {Group} from "../group/types";
|
||||
import { Group } from "../group/types";
|
||||
|
||||
export function* getUsers(action: ReturnType<typeof actions.getUsers.request>): Generator {
|
||||
try {
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
export function* getUsers(
|
||||
action: ReturnType<typeof actions.getUsers.request>
|
||||
): Generator {
|
||||
try {
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
|
||||
yield put(actions.getUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getUsers.failure(err as ApiError));
|
||||
}
|
||||
yield put(actions.getUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getUsers.failure(err as ApiError));
|
||||
}
|
||||
}
|
||||
|
||||
export function* getServiceUsers(action: ReturnType<typeof actions.getServiceUsers.request>): Generator {
|
||||
try {
|
||||
action.payload.queryParams = {service_user: true}
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
export function* getServiceUsers(
|
||||
action: ReturnType<typeof actions.getServiceUsers.request>
|
||||
): Generator {
|
||||
try {
|
||||
action.payload.queryParams = { service_user: true };
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
|
||||
yield put(actions.getServiceUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getServiceUsers.failure(err as ApiError));
|
||||
}
|
||||
yield put(actions.getServiceUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getServiceUsers.failure(err as ApiError));
|
||||
}
|
||||
}
|
||||
|
||||
export function* getRegularUsers(action: ReturnType<typeof actions.getRegularUsers.request>): Generator {
|
||||
try {
|
||||
action.payload.queryParams = {service_user: false}
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
export function* getRegularUsers(
|
||||
action: ReturnType<typeof actions.getRegularUsers.request>
|
||||
): Generator {
|
||||
try {
|
||||
action.payload.queryParams = { service_user: false };
|
||||
const effect = yield call(service.getUsers, action.payload);
|
||||
const response = effect as ApiResponse<User[]>;
|
||||
|
||||
yield put(actions.getRegularUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getRegularUsers.failure(err as ApiError));
|
||||
}
|
||||
yield put(actions.getRegularUsers.success(response.body));
|
||||
} catch (err) {
|
||||
yield put(actions.getRegularUsers.failure(err as ApiError));
|
||||
}
|
||||
}
|
||||
|
||||
export function* saveUser(action: ReturnType<typeof actions.saveUser.request>): Generator {
|
||||
try {
|
||||
yield put(actions.setSavedUser({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null
|
||||
} as CreateResponse<User | null>))
|
||||
export function* saveUser(
|
||||
action: ReturnType<typeof actions.saveUser.request>
|
||||
): Generator {
|
||||
try {
|
||||
yield put(
|
||||
actions.setSavedUser({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null,
|
||||
} as CreateResponse<User | null>)
|
||||
);
|
||||
|
||||
const userToSave = action.payload.payload
|
||||
const userToSave = action.payload.payload;
|
||||
|
||||
let groupsToCreate = userToSave.groupsToCreate
|
||||
if (!groupsToCreate) {
|
||||
groupsToCreate = []
|
||||
}
|
||||
|
||||
// first, create groups that were newly added by user
|
||||
const responsesGroup = yield all(groupsToCreate.map(g => call(serviceGroup.createGroup, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: {name: g}
|
||||
})
|
||||
))
|
||||
|
||||
const resGroups = (responsesGroup as ApiResponse<Group>[]).filter(r => r.statusCode === 200).map(g => (g.body as Group)).map(g => g.id)
|
||||
const newGroups = [...userToSave.auto_groups, ...resGroups]
|
||||
let payload = {
|
||||
name: userToSave.name,
|
||||
email: userToSave.email,
|
||||
role: userToSave.role,
|
||||
auto_groups: newGroups,
|
||||
is_service_user: userToSave.is_service_user,
|
||||
is_blocked: userToSave.is_blocked
|
||||
} as UserToSave
|
||||
|
||||
let effect
|
||||
if (!userToSave.id) {
|
||||
effect = yield call(service.createUser, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: payload
|
||||
});
|
||||
} else {
|
||||
payload.id = userToSave.id
|
||||
effect = yield call(service.editUser, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: payload
|
||||
});
|
||||
}
|
||||
const response = effect as ApiResponse<User>;
|
||||
|
||||
yield put(actions.saveUser.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body
|
||||
} as CreateResponse<User | null>));
|
||||
} catch (err) {
|
||||
yield put(actions.saveUser.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null
|
||||
} as CreateResponse<User | null>));
|
||||
let groupsToCreate = userToSave.groupsToCreate;
|
||||
if (!groupsToCreate) {
|
||||
groupsToCreate = [];
|
||||
}
|
||||
|
||||
// first, create groups that were newly added by user
|
||||
const responsesGroup = yield all(
|
||||
groupsToCreate.map((g) =>
|
||||
call(serviceGroup.createGroup, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: { name: g },
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const resGroups = (responsesGroup as ApiResponse<Group>[])
|
||||
.filter((r) => r.statusCode === 200)
|
||||
.map((g) => g.body as Group)
|
||||
.map((g) => g.id);
|
||||
const newGroups = [...userToSave.auto_groups, ...resGroups];
|
||||
let payload = {
|
||||
name: userToSave.name,
|
||||
email: userToSave.email,
|
||||
role: userToSave.role,
|
||||
auto_groups: newGroups,
|
||||
is_service_user: userToSave.is_service_user,
|
||||
is_blocked: userToSave.is_blocked,
|
||||
} as UserToSave;
|
||||
|
||||
let effect;
|
||||
if (!userToSave.id) {
|
||||
effect = yield call(service.createUser, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: payload,
|
||||
});
|
||||
} else {
|
||||
payload.id = userToSave.id;
|
||||
effect = yield call(service.editUser, {
|
||||
getAccessTokenSilently: action.payload.getAccessTokenSilently,
|
||||
payload: payload,
|
||||
});
|
||||
}
|
||||
const response = effect as ApiResponse<User>;
|
||||
|
||||
yield put(
|
||||
actions.saveUser.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body,
|
||||
} as CreateResponse<User | null>)
|
||||
);
|
||||
} catch (err) {
|
||||
yield put(
|
||||
actions.saveUser.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null,
|
||||
} as CreateResponse<User | null>)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function* deleteUser(action: ReturnType<typeof actions.deleteUser.request>): Generator {
|
||||
try {
|
||||
yield call(actions.setDeletedUser,{
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null
|
||||
} as DeleteResponse<string | null>)
|
||||
export function* deleteUser(
|
||||
action: ReturnType<typeof actions.deleteUser.request>
|
||||
): Generator {
|
||||
try {
|
||||
yield put(
|
||||
actions.setDeletedUser({
|
||||
loading: true,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: null,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
|
||||
const effect = yield call(service.deleteUser, action.payload);
|
||||
const response = effect as ApiResponse<any>;
|
||||
|
||||
yield put(actions.deleteUser.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body
|
||||
} as DeleteResponse<string | null>));
|
||||
|
||||
const users = (yield select(state => state.users.data)) as User[]
|
||||
const regularUsers = (yield select(state => state.users.regularUsers)) as User[]
|
||||
const serviceUsers = (yield select(state => state.users.serviceUsers)) as User[]
|
||||
yield put(actions.getUsers.success(users.filter((p:User) => p.id !== action.payload.payload)))
|
||||
yield put(actions.getRegularUsers.success(regularUsers.filter((p:User) => p.id !== action.payload.payload)))
|
||||
yield put(actions.getServiceUsers.success(serviceUsers.filter((p:User) => p.id !== action.payload.payload)))
|
||||
} catch (err) {
|
||||
yield put(actions.deleteUser.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null
|
||||
} as DeleteResponse<string | null>));
|
||||
}
|
||||
const effect = yield call(service.deleteUser, action.payload);
|
||||
const response = effect as ApiResponse<any>;
|
||||
yield put(
|
||||
actions.deleteUser.success({
|
||||
loading: false,
|
||||
success: true,
|
||||
failure: false,
|
||||
error: null,
|
||||
data: response.body,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
} catch (err) {
|
||||
yield put(
|
||||
actions.deleteUser.failure({
|
||||
loading: false,
|
||||
success: false,
|
||||
failure: false,
|
||||
error: err as ApiError,
|
||||
data: null,
|
||||
} as DeleteResponse<string | null>)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function* sagas(): Generator {
|
||||
yield all([
|
||||
takeLatest(actions.getUsers.request, getUsers),
|
||||
takeLatest(actions.getServiceUsers.request, getServiceUsers),
|
||||
takeLatest(actions.getRegularUsers.request, getRegularUsers),
|
||||
takeLatest(actions.saveUser.request, saveUser),
|
||||
takeLatest(actions.deleteUser.request, deleteUser)
|
||||
]);
|
||||
yield all([
|
||||
takeLatest(actions.getUsers.request, getUsers),
|
||||
takeLatest(actions.getServiceUsers.request, getServiceUsers),
|
||||
takeLatest(actions.getRegularUsers.request, getRegularUsers),
|
||||
takeLatest(actions.saveUser.request, saveUser),
|
||||
takeLatest(actions.deleteUser.request, deleteUser),
|
||||
]);
|
||||
}
|
||||
|
||||
+42
-51
@@ -168,37 +168,36 @@ export const Activity = () => {
|
||||
};
|
||||
const renderInitiator = (event: EventDataTable) => {
|
||||
let body = <></>;
|
||||
const user = users?.find((u) => u.id === event.initiator_id);
|
||||
if (event.initiator_id == "sys") {
|
||||
body = (
|
||||
<span
|
||||
style={{
|
||||
height: "auto",
|
||||
whiteSpace: "normal",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Text type="secondary">System</Text>
|
||||
</Row>
|
||||
</span>
|
||||
);
|
||||
<span
|
||||
style={{
|
||||
height: "auto",
|
||||
whiteSpace: "normal",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Text type="secondary">System</Text>
|
||||
</Row>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
switch (event.activity_code) {
|
||||
case "peer.login.expire":
|
||||
body = (
|
||||
<span
|
||||
style={{
|
||||
height: "auto",
|
||||
whiteSpace: "normal",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Text type="secondary">System</Text>
|
||||
</Row>
|
||||
</span>
|
||||
);
|
||||
<span
|
||||
style={{
|
||||
height: "auto",
|
||||
whiteSpace: "normal",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Text type="secondary">System</Text>
|
||||
</Row>
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
case "setupkey.peer.add":
|
||||
const key = setupKeys?.find((k) => k.id === event.initiator_id);
|
||||
@@ -224,7 +223,7 @@ export const Activity = () => {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (user) {
|
||||
if (event.initiator_name || event.initiator_email) {
|
||||
body = (
|
||||
<span
|
||||
style={{
|
||||
@@ -235,16 +234,16 @@ export const Activity = () => {
|
||||
>
|
||||
<Row>
|
||||
{" "}
|
||||
<Text>{user.name ? user.name : user.id}</Text>{" "}
|
||||
<Text>
|
||||
{event.initiator_name
|
||||
? event.initiator_name
|
||||
: event.initiator_id}
|
||||
</Text>{" "}
|
||||
</Row>
|
||||
<Row>
|
||||
{" "}
|
||||
<Text type="secondary">
|
||||
{user.email
|
||||
? user.email
|
||||
: user.is_service_user
|
||||
? "Service User"
|
||||
: "User"}
|
||||
{event.initiator_email ? event.initiator_email : "User"}
|
||||
</Text>{" "}
|
||||
</Row>
|
||||
</span>
|
||||
@@ -281,7 +280,6 @@ export const Activity = () => {
|
||||
) {
|
||||
return "-";
|
||||
}
|
||||
const user = users?.find((u) => u.id === event.target_id);
|
||||
switch (event.activity_code) {
|
||||
case "account.create":
|
||||
case "user.join":
|
||||
@@ -334,14 +332,10 @@ export const Activity = () => {
|
||||
case "user.group.add":
|
||||
case "user.group.delete":
|
||||
case "user.role.update":
|
||||
if (user) {
|
||||
if (event.meta.email || event.meta.username || event.target_id) {
|
||||
return renderMultiRowSpan(
|
||||
user.name ? user.name : user.id,
|
||||
user.email
|
||||
? user.email
|
||||
: user.is_service_user
|
||||
? "Service User"
|
||||
: "User"
|
||||
event.meta.username ? event.meta.username : event.target_id,
|
||||
event.meta.email ? event.meta.email : "User"
|
||||
);
|
||||
}
|
||||
if (event.meta.user_name) {
|
||||
@@ -365,14 +359,10 @@ export const Activity = () => {
|
||||
return renderMultiRowSpan("", "System setting");
|
||||
case "personal.access.token.create":
|
||||
case "personal.access.token.delete":
|
||||
if (user) {
|
||||
if (event.meta.email || event.meta.username || event.target_id) {
|
||||
return renderMultiRowSpan(
|
||||
user.name ? user.name : user.id,
|
||||
user.email
|
||||
? user.email
|
||||
: user.is_service_user
|
||||
? "Service User"
|
||||
: "User"
|
||||
event.meta.username ? event.meta.username : event.target_id,
|
||||
event.meta.email ? event.meta.email : "User"
|
||||
);
|
||||
}
|
||||
if (event.meta.user_name) {
|
||||
@@ -384,14 +374,15 @@ export const Activity = () => {
|
||||
return "-";
|
||||
case "service.user.create":
|
||||
case "service.user.delete":
|
||||
return renderMultiRowSpan(event.meta.name, "Service User");
|
||||
return renderMultiRowSpan(event.meta.username, "Service User");
|
||||
case "user.invite":
|
||||
case "user.block":
|
||||
case "user.delete":
|
||||
case "user.unblock":
|
||||
if (user) {
|
||||
if (event.meta.email || event.meta.username || event.target_id) {
|
||||
return renderMultiRowSpan(
|
||||
user.name ? user.name : user.id,
|
||||
user.email ? user.email : "User"
|
||||
event.meta.username ? event.meta.username : event.target_id,
|
||||
event.meta.email ? event.meta.email : "User"
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
+137
-25
@@ -31,7 +31,12 @@ 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, timeAgo} from "../utils/common";
|
||||
import {
|
||||
capitalize,
|
||||
isLocalDev,
|
||||
isNetBirdHosted,
|
||||
timeAgo,
|
||||
} from "../utils/common";
|
||||
import { usePageSizeHelpers } from "../utils/pageSize";
|
||||
import AddServiceUserPopup from "../components/popups/AddServiceUserPopup";
|
||||
import InviteUserPopup from "../components/popups/InviteUserPopup";
|
||||
@@ -56,13 +61,14 @@ export const RegularUsers = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [showGroupModal, setShowGroupModal] = useState(false);
|
||||
|
||||
const [showGroupModal, setShowGroupModal] = 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);
|
||||
const loading = useSelector((state: RootState) => state.user.loading);
|
||||
const deleteUser = useSelector((state: RootState) => state.user.deletedUser);
|
||||
|
||||
const updateUserDrawerVisible = useSelector(
|
||||
(state: RootState) => state.user.updateUserDrawerVisible
|
||||
);
|
||||
@@ -76,6 +82,42 @@ export const RegularUsers = () => {
|
||||
const [dataTable, setDataTable] = useState([] as UserDataTable[]);
|
||||
const [confirmModal, confirmModalContextHolder] = Modal.useModal();
|
||||
|
||||
const deleteKey = "deleting";
|
||||
useEffect(() => {
|
||||
const style = { marginTop: 85 };
|
||||
if (deleteUser.loading) {
|
||||
message.loading({ content: "Deleting...", key: deleteKey, style });
|
||||
} else if (deleteUser.success) {
|
||||
message.success({
|
||||
content: "User has been successfully deleted.",
|
||||
key: deleteKey,
|
||||
duration: 2,
|
||||
style,
|
||||
});
|
||||
dispatch(
|
||||
userActions.getRegularUsers.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
groupActions.getGroups.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
dispatch(userActions.resetDeletedUser(null));
|
||||
} else if (deleteUser.error) {
|
||||
message.error({
|
||||
content: "Failed to delete user.",
|
||||
key: deleteKey,
|
||||
duration: 2,
|
||||
style,
|
||||
});
|
||||
dispatch(userActions.resetDeletedUser(null));
|
||||
}
|
||||
}, [deleteUser]);
|
||||
|
||||
// setUserAndView makes the UserUpdate drawer visible (right side) and sets the user object
|
||||
const setUserAndView = (user: User) => {
|
||||
dispatch(userActions.setUpdateUserDrawerVisible(true));
|
||||
@@ -92,7 +134,7 @@ export const RegularUsers = () => {
|
||||
};
|
||||
|
||||
const setUserAndViewGroups = (user: User) => {
|
||||
dispatch(
|
||||
dispatch(
|
||||
userActions.setUser({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
@@ -101,8 +143,8 @@ export const RegularUsers = () => {
|
||||
name: user.name,
|
||||
is_current: user.is_current,
|
||||
} as User)
|
||||
);
|
||||
setShowGroupModal(true)
|
||||
);
|
||||
setShowGroupModal(true);
|
||||
};
|
||||
|
||||
const transformDataTable = (d: User[]): UserDataTable[] => {
|
||||
@@ -286,7 +328,7 @@ export const RegularUsers = () => {
|
||||
dispatch(userActions.setUpdateUserDrawerVisible(false));
|
||||
dispatch(userActions.setSavedUser({ ...savedUser, success: false }));
|
||||
dispatch(userActions.resetSavedUser(null));
|
||||
setShowGroupModal(false)
|
||||
setShowGroupModal(false);
|
||||
} else if (savedUser.error) {
|
||||
let errorMsg = "Failed to update user";
|
||||
switch (savedUser.error.statusCode) {
|
||||
@@ -354,7 +396,7 @@ export const RegularUsers = () => {
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
title: (
|
||||
<span className="font-500">
|
||||
Are you sure you want to block {user.name} ?
|
||||
Are you sure you want to block {user.name? user.name : user.id}?
|
||||
</span>
|
||||
),
|
||||
width: 600,
|
||||
@@ -405,6 +447,31 @@ export const RegularUsers = () => {
|
||||
} as UserToSave;
|
||||
};
|
||||
|
||||
const handleDeleteUser = (user: UserDataTable) => {
|
||||
confirmModal.confirm({
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
title: <span className="font-500">Are you sure you want to delete {user.name? user.name : user.id}?</span>,
|
||||
width: 500,
|
||||
content: (
|
||||
<Space direction="vertical" size="small">
|
||||
<Paragraph>
|
||||
Deleting this user will remove their devices and remove dashboard access.</Paragraph>
|
||||
</Space>
|
||||
),
|
||||
onOk() {
|
||||
dispatch(
|
||||
userActions.deleteUser.request({
|
||||
getAccessTokenSilently: getTokenSilently,
|
||||
payload: user.id,
|
||||
})
|
||||
);
|
||||
},
|
||||
onCancel() {
|
||||
// noop
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container style={{ padding: "0px" }}>
|
||||
@@ -499,19 +566,19 @@ export const RegularUsers = () => {
|
||||
defaultSortOrder="ascend"
|
||||
render={(text, record, index) => {
|
||||
const btn = (
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() =>
|
||||
handleEditUser(record as UserDataTable)
|
||||
}
|
||||
className="tooltip-label"
|
||||
>
|
||||
<Text className="font-500">
|
||||
{text && text.trim() !== ""
|
||||
? text
|
||||
: (record as User).id}
|
||||
</Text>
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() =>
|
||||
handleEditUser(record as UserDataTable)
|
||||
}
|
||||
className="tooltip-label"
|
||||
>
|
||||
<Text className="font-500">
|
||||
{text && text.trim() !== ""
|
||||
? text
|
||||
: (record as User).id}
|
||||
</Text>
|
||||
</Button>
|
||||
);
|
||||
|
||||
if ((record as User).is_current) {
|
||||
@@ -521,7 +588,7 @@ export const RegularUsers = () => {
|
||||
{btn}
|
||||
<Tag color="blue">me</Tag>
|
||||
</div>
|
||||
<Text type={"secondary"} style={{paddingLeft: "15px"}}>Last login: {String(timeAgo((record as User).last_login))}</Text>
|
||||
{((record as User).last_login && <Text type={"secondary"} style={{paddingLeft: "15px"}}>Last login: {String(timeAgo((record as User).last_login))}</Text>)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -533,7 +600,7 @@ export const RegularUsers = () => {
|
||||
{btn}
|
||||
<Tag color="gold">invited</Tag>
|
||||
</div>
|
||||
<Text type={"secondary"} style={{paddingLeft: "15px", paddingTop: "0px"}}>Last login: {String(timeAgo((record as User).last_login))}</Text>
|
||||
{((record as User).last_login && <Text type={"secondary"} style={{paddingLeft: "15px", paddingTop: "0px"}}>Last login: {String(timeAgo((record as User).last_login))}</Text>)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -545,12 +612,19 @@ export const RegularUsers = () => {
|
||||
{btn}
|
||||
<Tag color="red">blocked</Tag>
|
||||
</div>
|
||||
<Text type={"secondary"} style={{paddingLeft: "15px"}}>Last login: {String(timeAgo((record as User).last_login))}</Text>
|
||||
{((record as User).last_login && <Text type={"secondary"} style={{paddingLeft: "15px"}}>Last login: {String(timeAgo((record as User).last_login))}</Text>)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return btn;
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{btn}
|
||||
</div>
|
||||
{((record as User).last_login && <Text type={"secondary"} style={{paddingLeft: "15px", paddingTop: "0px"}}>Last login: {String(timeAgo((record as User).last_login))}</Text>)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
@@ -619,6 +693,44 @@ export const RegularUsers = () => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isAdmin && (
|
||||
<Column
|
||||
title="Delete user"
|
||||
align="center"
|
||||
width="150px"
|
||||
// dataIndex="is_blocked"
|
||||
render={(e, record: UserDataTable, index) => {
|
||||
let witch = (
|
||||
<Button
|
||||
danger={true}
|
||||
type={"text"}
|
||||
style={{ marginLeft: "3px", marginRight: "3px" }}
|
||||
onClick={() => {
|
||||
let userRecord = record as UserDataTable;
|
||||
handleDeleteUser(userRecord);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
);
|
||||
|
||||
if (record.is_current) {
|
||||
return (
|
||||
<Tooltip title="You can't delete yourself">
|
||||
<Empty
|
||||
image={""}
|
||||
description={""}
|
||||
style={{ height: "1px", width: "auto" }}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return witch;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Table>
|
||||
</Card>
|
||||
</Space>
|
||||
|
||||
+691
-507
File diff suppressed because it is too large.
Load diff
+58
-46
@@ -20,6 +20,7 @@ import {
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import { SetupKey, SetupKeyToSave } from "../store/setup-key/types";
|
||||
@@ -108,28 +109,27 @@ export const SetupKeys = () => {
|
||||
);
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setDataTable(transformDataTable(filterDataTable("")));
|
||||
}, [textToSearch, optionValidAll]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
const quickFilter = getFilterState("setupKeysFilter", "quickFilter");
|
||||
if (quickFilter) setOptionValidAll(quickFilter);
|
||||
const quickFilter = getFilterState("setupKeysFilter", "quickFilter");
|
||||
if (quickFilter) setOptionValidAll(quickFilter);
|
||||
|
||||
const searchText = getFilterState("setupKeysFilter", "search");
|
||||
if (searchText) setTextToSearch(searchText);
|
||||
const searchText = getFilterState("setupKeysFilter", "search");
|
||||
if (searchText) setTextToSearch(searchText);
|
||||
|
||||
const pageSize = getFilterState("setupKeysFilter", "pageSize");
|
||||
if (pageSize) onChangePageSize(pageSize, "setupKeysFilter");
|
||||
const pageSize = getFilterState("setupKeysFilter", "pageSize");
|
||||
if (pageSize) onChangePageSize(pageSize, "setupKeysFilter");
|
||||
|
||||
if (quickFilter || searchText || pageSize) {
|
||||
setDataTable(transformDataTable(filterDataTable(searchText)));
|
||||
} else {
|
||||
setDataTable(transformDataTable(filterDataTable("")));
|
||||
}
|
||||
}
|
||||
if (quickFilter || searchText || pageSize) {
|
||||
setDataTable(transformDataTable(filterDataTable(searchText)));
|
||||
} else {
|
||||
setDataTable(transformDataTable(filterDataTable("")));
|
||||
}
|
||||
}
|
||||
}, [loading]);
|
||||
|
||||
const deleteKey = "deleting";
|
||||
@@ -184,7 +184,7 @@ export const SetupKeys = () => {
|
||||
);
|
||||
dispatch(setupKeyActions.resetSavedSetupKey(null));
|
||||
dispatch(setupKeyActions.setSetupEditKeyVisible(false));
|
||||
setShowGroupModal(false)
|
||||
setShowGroupModal(false);
|
||||
} else if (savedSetupKey.error) {
|
||||
message.error({
|
||||
content:
|
||||
@@ -201,9 +201,9 @@ export const SetupKeys = () => {
|
||||
}, [savedSetupKey]);
|
||||
|
||||
const filterDataTable = (searchText: string): SetupKey[] => {
|
||||
const t = searchText
|
||||
? searchText.toLowerCase().trim()
|
||||
: textToSearch.toLowerCase().trim();
|
||||
const t = searchText
|
||||
? searchText.toLowerCase().trim()
|
||||
: textToSearch.toLowerCase().trim();
|
||||
let f: SetupKey[] = [...setupKeys];
|
||||
if (optionValidAll === "valid") {
|
||||
f = filter(setupKeys, (_f: SetupKey) => _f.valid && !_f.revoked);
|
||||
@@ -296,6 +296,7 @@ export const SetupKeys = () => {
|
||||
auto_groups: key?.auto_groups,
|
||||
last_used: key?.last_used,
|
||||
usage_limit: key?.usage_limit,
|
||||
ephemeral: key?.ephemeral,
|
||||
} as SetupKey)
|
||||
);
|
||||
};
|
||||
@@ -317,7 +318,7 @@ export const SetupKeys = () => {
|
||||
usage_limit: key?.usage_limit,
|
||||
} as SetupKey)
|
||||
);
|
||||
setShowGroupModal(true)
|
||||
setShowGroupModal(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -358,7 +359,9 @@ export const SetupKeys = () => {
|
||||
let btn = (
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => setKeyAndViewGroups(setupKeyToAction as SetupKeyDataTable)}
|
||||
onClick={() =>
|
||||
setKeyAndViewGroups(setupKeyToAction as SetupKeyDataTable)
|
||||
}
|
||||
>
|
||||
{displayGroups.length}
|
||||
</Button>
|
||||
@@ -576,14 +579,6 @@ export const SetupKeys = () => {
|
||||
(a as any).name.localeCompare((b as any).name)
|
||||
}
|
||||
render={(text, record: any, index) => {
|
||||
let sk = record as SetupKeyDataTable;
|
||||
let expiry = sk.ephemeral ? (
|
||||
<Tag>
|
||||
<Text type="secondary" style={{ fontSize: 10 }}>
|
||||
ephemeral peers enabled
|
||||
</Text>
|
||||
</Tag>
|
||||
) : null;
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
@@ -592,25 +587,22 @@ export const SetupKeys = () => {
|
||||
}
|
||||
className="tooltip-label"
|
||||
>
|
||||
<span style={{ textAlign: "left" }}>
|
||||
<Row>
|
||||
{" "}
|
||||
<Text className="font-500">
|
||||
<Badge
|
||||
size={"small"}
|
||||
status={
|
||||
record.state === "valid"
|
||||
? "success"
|
||||
: "error"
|
||||
}
|
||||
text={text}
|
||||
></Badge>
|
||||
</Text>
|
||||
</Row>
|
||||
<Row >
|
||||
{expiry}
|
||||
</Row>
|
||||
</span>
|
||||
<span style={{ textAlign: "left" }}>
|
||||
<Row>
|
||||
{" "}
|
||||
<Text className="font-500">
|
||||
<Badge
|
||||
size={"small"}
|
||||
status={
|
||||
record.state === "valid"
|
||||
? "success"
|
||||
: "error"
|
||||
}
|
||||
text={text}
|
||||
></Badge>
|
||||
</Text>
|
||||
</Row>
|
||||
</span>
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
@@ -625,6 +617,26 @@ export const SetupKeys = () => {
|
||||
sorter={(a, b) =>
|
||||
(a as any).type.localeCompare((b as any).type)
|
||||
}
|
||||
render={(text, record, index) => {
|
||||
let sk = record as SetupKeyDataTable;
|
||||
let expiry = sk.ephemeral ? (
|
||||
<Tooltip title="Peers that are offline for over 10 minutes will be removed automatically">
|
||||
<Tag>
|
||||
<Text type="secondary" style={{ fontSize: 10 }}>
|
||||
Ephemeral
|
||||
</Text>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
) : null;
|
||||
return (
|
||||
<>
|
||||
<div className="emp-wrapper">
|
||||
<p>{sk.type}</p>
|
||||
{expiry}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Column
|
||||
title="Key"
|
||||
|
||||
Reference in new issue
Block a user