From b50464db435fd6f2bf8f9be90da57cc33733cdb8 Mon Sep 17 00:00:00 2001 From: Crusadero <72073284+Crusadero@users.noreply.github.com> Date: Sun, 7 May 2023 19:39:28 +0300 Subject: [PATCH] Setup keys screen (#167) --- src/components/SetupKeyNew.tsx | 851 +++++++++++++++++++-------------- src/views/SetupKeys.tsx | 718 +++++++++++++++------------ 2 files changed, 904 insertions(+), 665 deletions(-) diff --git a/src/components/SetupKeyNew.tsx b/src/components/SetupKeyNew.tsx index 245d532..944ad5d 100644 --- a/src/components/SetupKeyNew.tsx +++ b/src/components/SetupKeyNew.tsx @@ -1,95 +1,74 @@ -import React, {useEffect, useRef, useState} from 'react'; -import {useDispatch, useSelector} from "react-redux"; -import {actions as setupKeyActions} from '../store/setup-key'; -import { - Button, - Col, - DatePicker, - DatePickerProps, - Divider, - Drawer, - Form, - Input, - InputNumber, - List, Modal, - Radio, - Row, - Select, - Space, - Tag, - Typography -} from "antd"; -import {RootState} from "typesafe-actions"; -import {CloseOutlined, EditOutlined, QuestionCircleFilled} from "@ant-design/icons"; -import {FormSetupKey, SetupKey, SetupKeyToSave} from "../store/setup-key/types"; -import {Header} from "antd/es/layout/layout"; -import {checkExpiresIn, formatDate, timeAgo} from "../utils/common"; -import {RuleObject} from "antd/lib/form"; -import {CustomTagProps} from "rc-select/lib/BaseSelect"; -import {Group} from "../store/group/types"; -import {useGetTokenSilently} from "../utils/token"; -import ExpiresInInput, {expiresInToSeconds, ExpiresInValue} from "../views/ExpiresInInput"; +import React, { useEffect, useRef, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { actions as setupKeyActions } from "../store/setup-key"; +import { Button, Col, Divider, Form, Input, InputNumber, Modal, Row, Select, Switch, Tag, Typography } from "antd"; +import { RootState } from "typesafe-actions"; +import { FormSetupKey, SetupKey, SetupKeyToSave } from "../store/setup-key/types"; +import { formatDate, timeAgo } from "../utils/common"; +import { RuleObject } from "antd/lib/form"; +import { CustomTagProps } from "rc-select/lib/BaseSelect"; +import { Group } from "../store/group/types"; +import { useGetTokenSilently } from "../utils/token"; +import { expiresInToSeconds, ExpiresInValue } from "../views/ExpiresInInput"; import moment from "moment"; +import { Container } from "./Container"; +import Paragraph from "antd/es/typography/Paragraph"; +import { EditOutlined, LockOutlined } from "@ant-design/icons"; -const {Option} = Select; +const { Option } = Select; +const { Text } = Typography; +const ExpiresInDefault: ExpiresInValue = { number: 30, interval: "day" }; -const {Text} = Typography; +const customExpiresFormat = (value: Date): string | null => { + return formatDate(value); +}; -const ExpiresInDefault: ExpiresInValue = {number: 30, interval: "day"} - -const customExpiresFormat: DatePickerProps['format'] = value => { - return formatDate(value) -} - -const customLastUsedFormat: DatePickerProps['format'] = value => { - if (value.year() == 1) { +const customLastUsedFormat = (value: Date): string | null => { + if (value.getFullYear() === 1) { // 1st of Jan 0001 - return "never" + return "never"; } - let ago = timeAgo(value.toString()) - if (!ago) { - return "unused" - } - return ago -} + + let ago = timeAgo(value.toString()); + if (!ago) return "unused"; + + return ago; +}; const SetupKeyNew = () => { - const {getTokenSilently} = useGetTokenSilently() - const dispatch = useDispatch() - const setupNewKeyVisible = useSelector((state: RootState) => state.setupKey.setupNewKeyVisible) - const setupKey = useSelector((state: RootState) => state.setupKey.setupKey) - const savedSetupKey = useSelector((state: RootState) => state.setupKey.savedSetupKey) - const groups = useSelector((state: RootState) => state.group.data) - const [editName, setEditName] = useState(false) - const inputNameRef = useRef(null) - const [selectedTagGroups, setSelectedTagGroups] = useState([] as string[]) - const [tagGroups, setTagGroups] = useState([] as string[]) + const { getTokenSilently } = useGetTokenSilently(); + const dispatch = useDispatch(); + const setupNewKeyVisible = useSelector((state: RootState) => state.setupKey.setupNewKeyVisible); + const setupKey = useSelector((state: RootState) => state.setupKey.setupKey); + const savedSetupKey = useSelector((state: RootState) => state.setupKey.savedSetupKey); + const groups = useSelector((state: RootState) => state.group.data); - const [formSetupKey, setFormSetupKey] = useState({} as FormSetupKey) - const [form] = Form.useForm() + const [form] = Form.useForm(); + const [editName, setEditName] = useState(false); + const [tagGroups, setTagGroups] = useState([] as string[]); + const [formSetupKey, setFormSetupKey] = useState({} as FormSetupKey); + const inputNameRef = useRef(null); + const isEditMode: boolean = !!formSetupKey.id; useEffect(() => { - if (editName) inputNameRef.current!.focus({ - cursor: 'end', - }); + if (!editName) return; + + inputNameRef.current!.focus({ cursor: "end" }); }, [editName]); useEffect(() => { - setTagGroups(groups?.filter(g => g.name != "All").map(g => g.name) || []) - }, [groups]) + setTagGroups(groups?.filter((g) => g.name !== "All").map((g) => g.name) || []); + }, [groups]); useEffect(() => { - if (!setupKey) return + if (!setupKey) return; - let allGroups = new Map(); - groups.forEach(g => { - allGroups.set(g.id!, g) - }) - - let formKeyGroups: string[] = [] + const allGroups = new Map(); + let formKeyGroups: string[] = []; + groups.forEach((g) => allGroups.set(g.id!, g)); if (setupKey.auto_groups) { - formKeyGroups = setupKey.auto_groups.filter(g => allGroups.get(g)).map(g => allGroups.get(g)!.name) + formKeyGroups = setupKey.auto_groups.filter((g) => allGroups.get(g)).map((g) => allGroups.get(g)!.name); } const fSetupKey = { @@ -97,19 +76,21 @@ const SetupKeyNew = () => { autoGroupNames: setupKey.auto_groups ? formKeyGroups : [], expiresInFormatted: ExpiresInDefault, exp: moment(setupKey.expires), - last: moment(setupKey.last_used) - } as FormSetupKey - setFormSetupKey(fSetupKey) - form.setFieldsValue(fSetupKey) - }, [setupKey]) + last: moment(setupKey.last_used), + } as FormSetupKey; + + form.setFieldsValue(fSetupKey); + setFormSetupKey(fSetupKey); + }, [setupKey]); const createSetupKeyToSave = (): SetupKeyToSave => { - const autoGroups = groups?.filter(g => formSetupKey.autoGroupNames.includes(g.name)).map(g => g.id || '') || [] + const autoGroups = + groups?.filter((g) => formSetupKey.autoGroupNames.includes(g.name)).map((g) => g.id || "") || []; // find groups that do not yet exist (newly added by the user) - const allGroupsNames: string[] = groups?.map(g => g.name); - const groupsToCreate = formSetupKey.autoGroupNames.filter(s => !allGroupsNames.includes(s)) + const allGroupsNames: string[] = groups?.map((g) => g.name); + const groupsToCreate = formSetupKey.autoGroupNames.filter((s) => !allGroupsNames.includes(s)); - let expiresIn = expiresInToSeconds(formSetupKey.expiresInFormatted) + const expiresIn = expiresInToSeconds(formSetupKey.expiresInFormatted); return { id: formSetupKey.id, name: formSetupKey.name, @@ -118,72 +99,79 @@ const SetupKeyNew = () => { revoked: formSetupKey.revoked, groupsToCreate: groupsToCreate, expires_in: expiresIn, - usage_limit: formSetupKey.usage_limit - } as SetupKeyToSave - } + usage_limit: formSetupKey.usage_limit, + } as SetupKeyToSave; + }; - const handleFormSubmit = () => { - form.validateFields() - .then((values) => { - let setupKeyToSave = createSetupKeyToSave() - dispatch(setupKeyActions.saveSetupKey.request({ - getAccessTokenSilently: getTokenSilently, - payload: setupKeyToSave - })) + const handleFormSubmit = async () => { + try { + await form.validateFields(); + } catch (e) { + const errorFields = (e as any).errorFields; + return console.log("errorInfo", errorFields); + } + + const setupKeyToSave = createSetupKeyToSave(); + dispatch( + setupKeyActions.saveSetupKey.request({ + getAccessTokenSilently: getTokenSilently, + payload: setupKeyToSave, }) - .catch((errorInfo) => { - console.log('errorInfo', errorInfo) - }); + ); }; const setVisibleNewSetupKey = (status: boolean) => { + form.resetFields(); dispatch(setupKeyActions.setSetupNewKeyVisible(status)); - } + }; const onCancel = () => { - if (savedSetupKey.loading) return - dispatch(setupKeyActions.setSetupKey({ - name: "", - type: "one-off", - key: "", - last_used: "", - expires: "", - state: "valid", - auto_groups: new Array(), - usage_limit: 0, - used_times: 0, - expires_in: 0 - } as SetupKey)) - setFormSetupKey({} as FormSetupKey) - setVisibleNewSetupKey(false) - } + if (savedSetupKey.loading) return; + + dispatch( + setupKeyActions.setSetupKey({ + name: "", + type: "one-off", + key: "", + last_used: "", + expires: "", + state: "valid", + auto_groups: [] as string[], + usage_limit: 0, + used_times: 0, + expires_in: 0, + } as SetupKey) + ); + setFormSetupKey({} as FormSetupKey); + setVisibleNewSetupKey(false); + }; const onChange = (data: any) => { - setFormSetupKey({...formSetupKey, ...data}) - } + setFormSetupKey({ ...formSetupKey, ...data }); + }; const toggleEditName = (status: boolean) => { setEditName(status); - } + }; const selectValidator = (_: RuleObject, value: string[]) => { - let hasSpaceNamed = [] + let hasSpaceNamed = []; value.forEach(function (v: string) { if (!v.trim().length) { - hasSpaceNamed.push(v) + hasSpaceNamed.push(v); } - }) + }); if (hasSpaceNamed.length) { - return Promise.reject(new Error("Group names with just spaces are not allowed")) + return Promise.reject(new Error("Group names with just spaces are not allowed")); } - return Promise.resolve() - } + return Promise.resolve(); + }; const tagRender = (props: CustomTagProps) => { - const {label, value, closable, onClose} = props; + const { value, closable, onClose } = props; const onPreventMouseDown = (event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); @@ -195,294 +183,421 @@ const SetupKeyNew = () => { onMouseDown={onPreventMouseDown} closable={closable} onClose={onClose} - style={{marginRight: 3}} + style={{ marginRight: 3 }} > {value} ); - } + }; const optionRender = (label: string) => { - let peersCount = '' - const g = groups.find(_g => _g.name === label) - if (g) peersCount = ` - ${g.peers_count || 0} ${(!g.peers_count || parseInt(g.peers_count) !== 1) ? 'peers' : 'peer'} ` + let peersCount = ""; + const g = groups.find((_g) => _g.name === label); + + if (g) { + peersCount = ` - ${g.peers_count || 0} ${ + !g.peers_count || parseInt(g.peers_count) !== 1 ? "peers" : "peer" + } `; + } + return ( <> - + {label} - {peersCount} + {peersCount} - ) - } + ); + }; const dropDownRender = (menu: React.ReactElement) => ( <> {menu} - - + + - Add new group by pressing "Enter" + Add new group by pressing "Enter" + fill="#9CA3AF" + /> - ) - - const handleChangeTags = (value: string[]) => { - let validatedValues: string[] = [] - value.forEach(function (v) { - if (v.trim().length) { - validatedValues.push(v) - } - }) - setSelectedTagGroups(validatedValues) - }; - - const inputLabel = (text: any) => ( - <> - {text} - {formSetupKey.state} - - ) + ); const changesDetected = (): boolean => { - return formSetupKey.name == null || formSetupKey.name !== setupKey.name || groupsChanged() - || formSetupKey.usage_limit !== setupKey.usage_limit - } + return ( + formSetupKey.name == null || + formSetupKey.name !== setupKey.name || + groupsChanged() || + formSetupKey.usage_limit !== setupKey.usage_limit + ); + }; const groupsChanged = (): boolean => { - if (setupKey && setupKey.auto_groups && formSetupKey.autoGroupNames.length != setupKey.auto_groups.length) { - return true + if (setupKey && setupKey.auto_groups && formSetupKey.autoGroupNames.length !== setupKey.auto_groups.length) { + return true; } - const formGroupIds = groups?.filter(g => formSetupKey.autoGroupNames.includes(g.name)).map(g => g.id || '') || [] + const formGroupIds = + groups?.filter((g) => formSetupKey.autoGroupNames.includes(g.name)).map((g) => g.id || "") || []; - return setupKey.auto_groups?.filter(g => !formGroupIds.includes(g)).length > 0 - } + return setupKey.auto_groups?.filter((g) => !formGroupIds.includes(g)).length > 0; + }; return ( - <> - {setupKey && - - - - - } + -
+ } + footer={[ + + + + , + ]} + > + + + + {isEditMode ? ( + <> + ) : ( -
- - - {!editName && setupKey.id && - - } - - - {!editName && setupKey.id && formSetupKey.name ? ( -
toggleEditName(true)}>{formSetupKey.name ? formSetupKey.name : setupKey.name} -
- ) : ( - - toggleEditName(false)} - onBlur={() => toggleEditName(false)} - autoComplete="off"/> - )} - -
-
- - {setupKey.id && formSetupKey.name && - - - Key - {formSetupKey.state} - } - > - - - - } - - {setupKey.id && formSetupKey.name && - - - - - - } - {setupKey.id && formSetupKey.name && - - - - - - } - - - - - - - - - One-off - This key can be used only once - - - - - - - Reusable - This type of a setup key allows to enroll multiple - machines - - - - - - - - - + Key name + + Name the key to identify it easily - {!setupKey.id && - - - - - } - - - + + {isEditMode ? ( + toggleEditName(true)} />} + onPressEnter={() => toggleEditName(false)} + onBlur={() => toggleEditName(false)} /> - - - - - - - + ) : ( + + )} + + +
+ + {isEditMode && ( + - - - - - - - + {formSetupKey.state} + + + } + /> - + )} -
- } - - ) -} + + {isEditMode ? ( + + + {formSetupKey.type === "one-off" ? "One-off" : "Reusable"} + + type + + ) : ( + + + + Reusable + + + This type of a setup key allows to enroll multiple peers + + + + { + setFormSetupKey({ + ...formSetupKey, + type: checked ? "reusable" : "one-off", + }); + }} + /> + + + )} + -export default SetupKeyNew \ No newline at end of file + + + + {isEditMode ? "Available uses" : "Usage limit"} + + + + {isEditMode ? ( + } + style={{ width: "104px" }} + /> + ) : ( + + + + )} + + {isEditMode ? ( + <> + ) : ( + + + 1 usage = 1 peer. E.g., set to 30 if you want to enroll 30 peers + + + )} + + + + {isEditMode ? ( + + + + + Expires + + + } + value={customExpiresFormat(new Date(formSetupKey.expires))!} + /> + + + + + Last used + + + } + value={customLastUsedFormat(new Date(formSetupKey.last_used))!} + /> + + + + + ) : ( + <> + + + Expires in + + + + + + + + Days + + + + + + + Should be between 1 and 180 days + + + )} + + + + + + Auto-assigned groups + + {isEditMode ? ( + <> + ) : ( + + These groups will be automatically assigned to peers enrolled with this key + + )} + + + + + + + + + + Learn more about + + {" "} + Setup Keys + + + + + + + ); +}; + +export default SetupKeyNew; diff --git a/src/views/SetupKeys.tsx b/src/views/SetupKeys.tsx index 22cdefd..ddad657 100644 --- a/src/views/SetupKeys.tsx +++ b/src/views/SetupKeys.tsx @@ -1,16 +1,14 @@ -import React, {useEffect, useState} from 'react'; -import {useDispatch, useSelector} from "react-redux"; -import {RootState} from "typesafe-actions"; -import {actions as setupKeyActions} from '../store/setup-key'; -import {Container} from "../components/Container"; +import React, { useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { RootState } from "typesafe-actions"; +import { actions as setupKeyActions } from "../store/setup-key"; +import { Container } from "../components/Container"; import { Alert, Button, Card, Col, - Dropdown, Input, - Menu, message, Modal, Popover, @@ -21,330 +19,334 @@ import { Space, Table, Tag, - Typography + Typography, } from "antd"; -import {SetupKey, SetupKeyToSave} from "../store/setup-key/types"; -import {filter} from "lodash" -import {formatDate, timeAgo} from "../utils/common"; -import {ExclamationCircleOutlined} from "@ant-design/icons"; +import { SetupKey, SetupKeyToSave } from "../store/setup-key/types"; +import { filter } from "lodash"; +import { formatDate, timeAgo } from "../utils/common"; +import { ExclamationCircleOutlined } from "@ant-design/icons"; import SetupKeyNew from "../components/SetupKeyNew"; import ButtonCopyMessage from "../components/ButtonCopyMessage"; import tableSpin from "../components/Spin"; -import {actions as groupActions} from "../store/group"; -import {Group} from "../store/group/types"; -import {TooltipPlacement} from "antd/es/tooltip"; -import {useGetTokenSilently} from "../utils/token"; -import {usePageSizeHelpers} from "../utils/pageSize"; +import { actions as groupActions } from "../store/group"; +import { Group } from "../store/group/types"; +import { TooltipPlacement } from "antd/es/tooltip"; +import { useGetTokenSilently } from "../utils/token"; +import { usePageSizeHelpers } from "../utils/pageSize"; -const {Title, Text, Paragraph} = Typography; -const {Column} = Table; -const {confirm} = Modal; +const { Title, Text, Paragraph } = Typography; +const { Column } = Table; interface SetupKeyDataTable extends SetupKey { - key: string - groupsCount: number + key: string; + groupsCount: number; } export const SetupKeys = () => { - const {onChangePageSize,pageSizeOptions,pageSize} = usePageSizeHelpers() - const {getTokenSilently} = useGetTokenSilently() - const dispatch = useDispatch() + const { onChangePageSize, pageSizeOptions, pageSize } = usePageSizeHelpers(); + const { getTokenSilently } = useGetTokenSilently(); + const dispatch = useDispatch(); const setupKeys = useSelector((state: RootState) => state.setupKey.data); const failed = useSelector((state: RootState) => state.setupKey.failed); const loading = useSelector((state: RootState) => state.setupKey.loading); const deletedSetupKey = useSelector((state: RootState) => state.setupKey.deletedSetupKey); const savedSetupKey = useSelector((state: RootState) => state.setupKey.savedSetupKey); - const groups = useSelector((state: RootState) => state.group.data) + const groups = useSelector((state: RootState) => state.group.data); - const [textToSearch, setTextToSearch] = useState(''); - const [optionValidAll, setOptionValidAll] = useState('valid'); + const [textToSearch, setTextToSearch] = useState(""); + const [optionValidAll, setOptionValidAll] = useState("valid"); const [dataTable, setDataTable] = useState([] as SetupKeyDataTable[]); - const [setupKeyToAction, setSetupKeyToAction] = useState(null as SetupKeyDataTable | null); - const setupNewKeyVisible = useSelector((state: RootState) => state.setupKey.setupNewKeyVisible) - const [groupPopupVisible,setGroupPopupVisible] = useState("") + const setupNewKeyVisible = useSelector((state: RootState) => state.setupKey.setupNewKeyVisible); + const [groupPopupVisible, setGroupPopupVisible] = useState(""); - const styleNotification = {marginTop: 85} + const styleNotification = { marginTop: 85 }; + const showTutorial = !dataTable.length; + const optionsValidAll = [ + { label: "Valid", value: "valid" }, + { label: "All", value: "all" }, + ]; - const optionsValidAll = [{label: 'Valid', value: 'valid'}, {label: 'All', value: 'all'}] - - const itemsMenuAction = [ - { - key: "revoke", - label: () - }, - { - key: "edit", - label: () - }, - - ] - const actionsMenu = () const [confirmModal, confirmModalContextHolder] = Modal.useModal(); const transformDataTable = (d: SetupKey[]): SetupKeyDataTable[] => { - return d.map(p => ({...p, groupsCount: p.auto_groups ? p.auto_groups.length : 0} as SetupKeyDataTable)) - } + return d.map((p) => ({ ...p, groupsCount: p.auto_groups ? p.auto_groups.length : 0 } as SetupKeyDataTable)); + }; useEffect(() => { - dispatch(setupKeyActions.getSetupKeys.request({getAccessTokenSilently: getTokenSilently, payload: null})); - dispatch(groupActions.getGroups.request({getAccessTokenSilently: getTokenSilently, payload: null})); - }, []) + dispatch(setupKeyActions.getSetupKeys.request({ getAccessTokenSilently: getTokenSilently, payload: null })); + dispatch(groupActions.getGroups.request({ getAccessTokenSilently: getTokenSilently, payload: null })); + }, []); useEffect(() => { - setDataTable(transformDataTable(filterDataTable())) - }, [setupKeys]) + setDataTable(transformDataTable(filterDataTable())); + }, [setupKeys]); useEffect(() => { - setDataTable(transformDataTable(filterDataTable())) - }, [textToSearch, optionValidAll]) + setDataTable(transformDataTable(filterDataTable())); + }, [textToSearch, optionValidAll]); - const deleteKey = 'deleting'; + const deleteKey = "deleting"; useEffect(() => { if (deletedSetupKey.loading) { - message.loading({content: 'Deleting...', key: deleteKey, style: styleNotification}); + message.loading({ content: "Deleting...", key: deleteKey, style: styleNotification }); } else if (deletedSetupKey.success) { message.success({ - content: 'Setup key has been successfully removed.', + content: "Setup key has been successfully removed.", key: deleteKey, duration: 2, - style: styleNotification + style: styleNotification, }); - dispatch(setupKeyActions.setDeleteSetupKey({...deletedSetupKey, success: false})) - dispatch(setupKeyActions.resetDeletedSetupKey(null)) + dispatch(setupKeyActions.setDeleteSetupKey({ ...deletedSetupKey, success: false })); + dispatch(setupKeyActions.resetDeletedSetupKey(null)); } else if (deletedSetupKey.error) { message.error({ - content: 'Failed to delete setup key. You might not have enough permissions.', + content: "Failed to delete setup key. You might not have enough permissions.", key: deleteKey, duration: 2, - style: styleNotification + style: styleNotification, }); - dispatch(setupKeyActions.setDeleteSetupKey({...deletedSetupKey, error: null})) - dispatch(setupKeyActions.resetDeletedSetupKey(null)) + dispatch(setupKeyActions.setDeleteSetupKey({ ...deletedSetupKey, error: null })); + dispatch(setupKeyActions.resetDeletedSetupKey(null)); } - }, [deletedSetupKey]) + }, [deletedSetupKey]); - const createKey = 'saving'; + const createKey = "saving"; useEffect(() => { if (savedSetupKey.loading) { - message.loading({content: 'Saving...', key: createKey, duration: 0, style: styleNotification}); + message.loading({ content: "Saving...", key: createKey, duration: 0, style: styleNotification }); } else if (savedSetupKey.success) { message.success({ - content: 'Setup key has been successfully saved.', + content: "Setup key has been successfully saved.", key: createKey, duration: 2, - style: styleNotification + style: styleNotification, }); dispatch(setupKeyActions.setSetupNewKeyVisible(false)); - dispatch(setupKeyActions.setSavedSetupKey({...savedSetupKey, success: false})); - dispatch(setupKeyActions.resetSavedSetupKey(null)) + dispatch(setupKeyActions.setSavedSetupKey({ ...savedSetupKey, success: false })); + dispatch(setupKeyActions.resetSavedSetupKey(null)); } else if (savedSetupKey.error) { message.error({ - content: 'Failed to update setup key. You might not have enough permissions.', + content: "Failed to update setup key. You might not have enough permissions.", key: createKey, duration: 2, - style: styleNotification + style: styleNotification, }); - dispatch(setupKeyActions.setSavedSetupKey({...savedSetupKey, error: null})); - dispatch(setupKeyActions.resetSavedSetupKey(null)) + dispatch(setupKeyActions.setSavedSetupKey({ ...savedSetupKey, error: null })); + dispatch(setupKeyActions.resetSavedSetupKey(null)); } - }, [savedSetupKey]) + }, [savedSetupKey]); const filterDataTable = (): SetupKey[] => { - const t = textToSearch.toLowerCase().trim() - let f: SetupKey[] = [...setupKeys] + const t = textToSearch.toLowerCase().trim(); + let f: SetupKey[] = [...setupKeys]; if (optionValidAll === "valid") { - f = filter(setupKeys, (_f: SetupKey) => _f.valid && !_f.revoked) + f = filter(setupKeys, (_f: SetupKey) => _f.valid && !_f.revoked); } - f = filter(f, (_f: SetupKey) => - (_f.name.toLowerCase().includes(t) || _f.state.includes(t) || _f.type.toLowerCase().includes(t) || _f.key.toLowerCase().includes(t) || t === "") - ) as SetupKey[] - return f - } + f = filter( + f, + (_f: SetupKey) => + _f.name.toLowerCase().includes(t) || + _f.state.includes(t) || + _f.type.toLowerCase().includes(t) || + _f.key.toLowerCase().includes(t) || + t === "" + ) as SetupKey[]; + return f; + }; const onChangeTextToSearch = (e: React.ChangeEvent) => { - setTextToSearch(e.target.value) + setTextToSearch(e.target.value); }; const searchDataTable = () => { - const data = filterDataTable() - setDataTable(transformDataTable(data)) - } + const data = filterDataTable(); + setDataTable(transformDataTable(data)); + }; - const onChangeValidAll = ({target: {value}}: RadioChangeEvent) => { - setOptionValidAll(value) - } + const onChangeValidAll = ({ target: { value } }: RadioChangeEvent) => { + setOptionValidAll(value); + }; - const showConfirmRevoke = () => { - let name = setupKeyToAction ? setupKeyToAction.name : '' + const showConfirmRevoke = (setupKeyToAction: SetupKeyDataTable) => { + let name = setupKeyToAction ? setupKeyToAction.name : ""; confirmModal.confirm({ - icon: , - title: "Revoke setupKey \"" + name + "\"", + icon: , + title: 'Revoke setupKey "' + name + '"', width: 600, - content: - Are you sure you want to revoke key? - , + content: ( + + Are you sure you want to revoke key? + + ), onOk() { - dispatch(setupKeyActions.saveSetupKey.request({ - getAccessTokenSilently: getTokenSilently, - payload: { - id: setupKeyToAction ? setupKeyToAction.id : null, - revoked: true, - name: setupKeyToAction ? setupKeyToAction.name : null, - auto_groups: setupKeyToAction && setupKeyToAction.auto_groups ? setupKeyToAction.auto_groups : [], - } as SetupKeyToSave - })); - }, - onCancel() { - setSetupKeyToAction(null); + dispatch( + setupKeyActions.saveSetupKey.request({ + getAccessTokenSilently: getTokenSilently, + payload: { + id: setupKeyToAction ? setupKeyToAction.id : null, + revoked: true, + name: setupKeyToAction ? setupKeyToAction.name : null, + auto_groups: + setupKeyToAction && setupKeyToAction.auto_groups ? setupKeyToAction.auto_groups : [], + } as SetupKeyToSave, + }) + ); }, }); - } + }; const onClickAddNewSetupKey = () => { - const autoGroups : string[] = [] + const autoGroups: string[] = []; dispatch(setupKeyActions.setSetupNewKeyVisible(true)); - dispatch(setupKeyActions.setSetupKey({ - name: "", - type: "one-off", - auto_groups: autoGroups - } as SetupKey)) - } + dispatch( + setupKeyActions.setSetupKey({ + name: "", + type: "one-off", + auto_groups: autoGroups, + } as SetupKey) + ); + }; const setKeyAndView = (key: SetupKeyDataTable) => { dispatch(setupKeyActions.setSetupNewKeyVisible(true)); - dispatch(setupKeyActions.setSetupKey({ - id: key?.id || null, - key: key?.key, - name: key?.name, - revoked: key?.revoked, - expires: key?.expires, - state: key?.state, - type: key?.type, - used_times: key?.used_times, - valid: key?.valid, - auto_groups: key?.auto_groups, - last_used: key?.last_used, - usage_limit: key?.usage_limit - } as SetupKey)) - } - - const onClickEditSetupKey = () => { - dispatch(setupKeyActions.setSetupNewKeyVisible(true)); - dispatch(setupKeyActions.setSetupKey({ - id: setupKeyToAction?.id || null, - key: setupKeyToAction?.key, - name: setupKeyToAction?.name, - revoked: setupKeyToAction?.revoked, - expires: setupKeyToAction?.expires, - state: setupKeyToAction?.state, - type: setupKeyToAction?.type, - used_times: setupKeyToAction?.used_times, - valid: setupKeyToAction?.valid, - auto_groups: setupKeyToAction?.auto_groups, - last_used: setupKeyToAction?.last_used, - usage_limit: setupKeyToAction?.usage_limit - } as SetupKey)) - } + dispatch( + setupKeyActions.setSetupKey({ + id: key?.id || null, + key: key?.key, + name: key?.name, + revoked: key?.revoked, + expires: key?.expires, + state: key?.state, + type: key?.type, + used_times: key?.used_times, + valid: key?.valid, + auto_groups: key?.auto_groups, + last_used: key?.last_used, + usage_limit: key?.usage_limit, + } as SetupKey) + ); + }; useEffect(() => { if (setupNewKeyVisible) { - setGroupPopupVisible("") + setGroupPopupVisible(""); } - }, [setupNewKeyVisible]) + }, [setupNewKeyVisible]); - const onPopoverVisibleChange = (b:boolean, key: string) => { + const onPopoverVisibleChange = (b: boolean, key: string) => { if (setupNewKeyVisible) { - setGroupPopupVisible("") + setGroupPopupVisible(""); } else { if (b) { - setGroupPopupVisible(key) + setGroupPopupVisible(key); } else { - setGroupPopupVisible("") + setGroupPopupVisible(""); } } - } - - const renderPopoverGroups = (label: string, rowGroups: string[] | string[] | null, setupKeyToAction: SetupKeyDataTable) => { + }; + const renderPopoverGroups = ( + label: string, + rowGroups: string[] | string[] | null, + setupKeyToAction: SetupKeyDataTable + ) => { let groupsMap = new Map(); - groups.forEach(g => { - groupsMap.set(g.id!, g) - }) + groups.forEach((g) => { + groupsMap.set(g.id!, g); + }); - let displayGroups :Group[] = [] + let displayGroups: Group[] = []; if (rowGroups) { - displayGroups = rowGroups.filter(g => groupsMap.get(g)).map(g => groupsMap.get(g)!) + displayGroups = rowGroups.filter((g) => groupsMap.get(g)).map((g) => groupsMap.get(g)!); } - let btn = + let btn = ( + + ); if (!displayGroups || displayGroups!.length < 1) { - return btn + return btn; } const content = displayGroups?.map((g, i) => { - const _g = g as Group - const peersCount = ` - ${_g.peers_count || 0} ${(!_g.peers_count || parseInt(_g.peers_count) !== 1) ? 'peers' : 'peer'} ` + const _g = g as Group; + const peersCount = ` - ${_g.peers_count || 0} ${ + !_g.peers_count || parseInt(_g.peers_count) !== 1 ? "peers" : "peer" + } `; return (
- + {_g.name} - {peersCount} + {peersCount}
- ) - }) - const mainContent = ({content}) - let popoverPlacement = "top" + ); + }); + const mainContent = {content}; + let popoverPlacement = "top"; if (content && content.length > 5) { - popoverPlacement = "rightTop" + popoverPlacement = "rightTop"; } return ( - onPopoverVisibleChange(b, setupKeyToAction.key)} - open={groupPopupVisible === setupKeyToAction.key} - content={mainContent} - title={null}> + onPopoverVisibleChange(b, setupKeyToAction.key)} + open={groupPopupVisible === setupKeyToAction.key} + content={mainContent} + title={null} + > {btn} - ) - } + ); + }; const setUpdateGroupsVisible = (setupKeyToAction: SetupKey, status: boolean) => { if (status) { - dispatch(setupKeyActions.setSetupKey({...setupKeyToAction})) - dispatch(setupKeyActions.setSetupNewKeyVisible(true)) - return + dispatch(setupKeyActions.setSetupKey({ ...setupKeyToAction })); + dispatch(setupKeyActions.setSetupNewKeyVisible(true)); + return; } - const autoGroups : string[] = [] - dispatch(setupKeyActions.setSetupKey({ - name: "", - type: "one-off", - auto_groups: autoGroups - } as SetupKey)) - dispatch(setupKeyActions.setSetupNewKeyVisible(false)) - } + const autoGroups: string[] = []; + dispatch( + setupKeyActions.setSetupKey({ + name: "", + type: "one-off", + auto_groups: autoGroups, + } as SetupKey) + ); + dispatch(setupKeyActions.setSetupNewKeyVisible(false)); + }; return ( <> - + Setup Keys - A list of all the setup keys in your account including their name, state, type and - expiration. - + + A list of all the setup keys in your account including their name, state, type and + expiration. + + {/**/} - + @@ -354,118 +356,240 @@ export const SetupKeys = () => { value={optionValidAll} optionType="button" buttonStyle="solid" + disabled={!dataTable?.length} + /> + - - - - - - - - - {failed && - - } - - `Showing ${range[0]} to ${range[1]} of ${total} setup keys`) - }} - className="card-table" - showSorterTooltip={false} - scroll={{x: true}} - loading={tableSpin(loading)} - dataSource={dataTable}> - (record as any).name.includes(value)} - sorter={(a, b) => ((a as any).name.localeCompare((b as any).name))} - render={(text, record, index) => { - return + + + + + + + ) : ( + <> + )} + + {failed && ( + + )} + {showTutorial ? ( + + + + + Create Setup Key + + + + + Manage Setup Keys to register new machines in your network. The key + links the machine to an account during initial setup. + + {" "} + Learn more + + + + - - + + + + + ) : ( + +
+ `Showing ${range[0]} to ${range[1]} of ${total} setup keys`, + }} + className="card-table" + showSorterTooltip={false} + scroll={{ x: true }} + loading={tableSpin(loading)} + dataSource={dataTable} + > + + (record as any).name.includes(value) + } + sorter={(a, b) => (a as any).name.localeCompare((b as any).name)} render={(text, record, index) => { - return (text === 'valid') ? {text} : - {text} + return ( + + ); }} - sorter={(a, b) => ((a as any).state.localeCompare((b as any).state))} - /> + defaultSortOrder="ascend" + /> + + (record as any).type.includes(value) + } + sorter={(a, b) => (a as any).type.localeCompare((b as any).type)} + /> + + (record as any).key.includes(value) + } + sorter={(a, b) => (a as any).key.localeCompare((b as any).key)} + render={(text, record, index) => { + const body = {text}; + return ( + + ); + }} + /> - (record as any).type.includes(value)} - sorter={(a, b) => ((a as any).type.localeCompare((b as any).type))} - /> - (a as any).last_used.localeCompare((b as any).last_used)} + render={(text, record, index) => { + return !(record as SetupKey).used_times ? "never" : timeAgo(text); + }} + /> + { - return renderPopoverGroups(text, record.auto_groups, record) + return renderPopoverGroups(text, record.auto_groups, record); }} - /> - (record as any).key.includes(value)} - sorter={(a, b) => ((a as any).key.localeCompare((b as any).key))} + /> + { - const body = {text} - return + return formatDate(text); }} - /> - - ((a as any).last_used.localeCompare((b as any).last_used))} + /> + { - return !(record as SetupKey).used_times ? 'never' : timeAgo(text) + return text === "valid" ? ( + {text} + ) : ( + {text} + ); }} - /> - ((a as any).used_times - ((b as any).used_times))} - /> - - (a as any).state.localeCompare((b as any).state)} + /> + { - return formatDate(text) + return ( + + ); }} - /> - - { - return !(record as SetupKeyDataTable).revoked ? ( - { - if (visible) setSetupKeyToAction(record as SetupKeyDataTable) - }}>) : <> - }} - /> -
-
+ /> + + + )}
-
- + {setupNewKeyVisible && } {confirmModalContextHolder} - ) -} + ); +}; -export default SetupKeys; \ No newline at end of file +export default SetupKeys;