Compare commits

...
Author SHA1 Message Date
Sarooj Bukhari 8ad6952dae da 2023-08-25 14:44:10 +05:00
Sarooj Bukhari 7e5595c365 fix button hover 2023-08-15 21:39:36 +05:00
Sarooj Bukhari 12687379cc limit user account 2023-08-15 21:04:52 +05:00
braginini 62d80af186 Limit columns and navbar 2023-08-15 15:37:37 +02:00
9 changed files with 394 additions and 207 deletions

No files matched your search

+33
View File
@@ -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
View File
@@ -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"
} }
} }
View File
Whitespace-only changes.
+1
View File
@@ -53,6 +53,7 @@ const Navbar = () => {
"/acls", "/acls",
"/routes", "/routes",
"/dns", "/dns",
"/users",
"/activity", "/activity",
"/settings", "/settings",
]; ];
+3 -2
View File
@@ -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"
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>