From 227a51423d2ab4a44cde2dcdd85465e249596f87 Mon Sep 17 00:00:00 2001 From: Charles Date: Fri, 3 Feb 2023 16:53:50 +0100 Subject: [PATCH] [WIFI-12261] Added system secrets on the system page Signed-off-by: Charles --- package-lock.json | 4 +- package.json | 2 +- public/locales/de/translation.json | 6 +- public/locales/en/translation.json | 6 +- public/locales/es/translation.json | 6 +- public/locales/fr/translation.json | 6 +- public/locales/pt/translation.json | 6 +- src/components/DataTables/DataTable/index.tsx | 23 ++- .../DataTables/SortableDataTable/index.tsx | 9 +- .../SystemPage/SystemSecrets/Actions.tsx | 139 +++++++++++++++++ .../SystemPage/SystemSecrets/CreateButton.tsx | 118 ++++++++++++++ .../SystemPage/SystemSecrets/EditButton.tsx | 146 ++++++++++++++++++ src/pages/SystemPage/SystemSecrets/Table.tsx | 70 +++++++++ src/pages/SystemPage/SystemSecrets/index.tsx | 53 +++++++ .../SystemTile/LoggingButton/Modal.tsx | 2 +- .../SystemTile/SystemCertificatesTable.tsx | 2 +- src/pages/SystemPage/SystemTile/index.tsx | 8 +- src/pages/SystemPage/index.tsx | 93 ++++++++--- 18 files changed, 653 insertions(+), 46 deletions(-) create mode 100644 src/pages/SystemPage/SystemSecrets/Actions.tsx create mode 100644 src/pages/SystemPage/SystemSecrets/CreateButton.tsx create mode 100644 src/pages/SystemPage/SystemSecrets/EditButton.tsx create mode 100644 src/pages/SystemPage/SystemSecrets/Table.tsx create mode 100644 src/pages/SystemPage/SystemSecrets/index.tsx diff --git a/package-lock.json b/package-lock.json index 5f7f3b3..1c51ebc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ucentral-client", - "version": "2.9.0(7)", + "version": "2.9.0(9)", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ucentral-client", - "version": "2.9.0(7)", + "version": "2.9.0(9)", "license": "ISC", "dependencies": { "@chakra-ui/icons": "^2.0.11", diff --git a/package.json b/package.json index 878a169..d4cd29d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ucentral-client", - "version": "2.9.0(7)", + "version": "2.9.0(9)", "description": "", "private": true, "main": "index.tsx", diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index 94617ca..8c8f65a 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -1037,6 +1037,7 @@ }, "system": { "backend_logs": "Back-End-Protokolle", + "configuration": "Aufbau", "could_not_retrieve": "Fehler: {{name}} Systeminformationen konnten nicht abgerufen werden", "endpoint": "Endpunkt", "hostname": "Hostname", @@ -1047,9 +1048,10 @@ "os": "Betriebssystem", "processors": "Prozessoren", "reload_chosen_subsystems": "Ausgewählte Subsysteme neu laden", - "secrets": "Systemgeheimnisse", + "secrets": "Geheimnisse", "secrets_create": "Geheimnis erstellen", - "secrets_one": "Systemgeheimnis", + "secrets_one": "Geheimnis", + "services": "dienstleistungen", "start": "Start", "subsystems": "Subsysteme", "success_reload": "Reload-Befehl erfolgreich gesendet!", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index c362e5a..f6b7e05 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -1037,6 +1037,7 @@ }, "system": { "backend_logs": "Back-End Logs", + "configuration": "Configuration", "could_not_retrieve": "Error: could not retrieve {{name}} system information", "endpoint": "Endpoint", "hostname": "Host Name", @@ -1047,9 +1048,10 @@ "os": "Operating System", "processors": "Processors", "reload_chosen_subsystems": "Reload Chosen Subsystems", - "secrets": "System Secrets", + "secrets": "Secrets", "secrets_create": "Create Secret", - "secrets_one": "System Secret", + "secrets_one": "Secret", + "services": "Services", "start": "Start", "subsystems": "Subsystems", "success_reload": "Successfully sent reload command!", diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index 19a29fe..1a5385b 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -1037,6 +1037,7 @@ }, "system": { "backend_logs": "Registros de back-end", + "configuration": "Configuración", "could_not_retrieve": "Error: no se pudo recuperar la información del sistema {{name}} ", "endpoint": "punto final", "hostname": "Nombre de host", @@ -1047,9 +1048,10 @@ "os": "sistema operativo", "processors": "Procesadores", "reload_chosen_subsystems": "Recargar subsistemas elegidos", - "secrets": "Secretos del sistema", + "secrets": "Misterios", "secrets_create": "Crear secreto", - "secrets_one": "Secreto del sistema", + "secrets_one": "secreto", + "services": "Servicios", "start": "comienzo", "subsystems": "Subsistemas", "success_reload": "¡Comando de recarga enviado con éxito!", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index ec87c4a..7339cc2 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -1037,6 +1037,7 @@ }, "system": { "backend_logs": "Journaux principaux", + "configuration": "Configuration", "could_not_retrieve": "Erreur : impossible de récupérer les informations système {{name}} ", "endpoint": "Point final", "hostname": "nom d'hôte", @@ -1047,9 +1048,10 @@ "os": "Système opérateur", "processors": "Processeurs", "reload_chosen_subsystems": "Recharger les sous-systèmes choisis", - "secrets": "Secrets du système", + "secrets": "Secrets", "secrets_create": "Créer un secret", - "secrets_one": "Code secret du système", + "secrets_one": "Secret", + "services": "Prestations de service", "start": "Début", "subsystems": "Sous-systèmes", "success_reload": "Commande de rechargement envoyée avec succès !", diff --git a/public/locales/pt/translation.json b/public/locales/pt/translation.json index f8d9e1f..082b30c 100644 --- a/public/locales/pt/translation.json +++ b/public/locales/pt/translation.json @@ -1037,6 +1037,7 @@ }, "system": { "backend_logs": "Registros de back-end", + "configuration": "Configuração", "could_not_retrieve": "Erro: não foi possível recuperar {{name}} informações do sistema", "endpoint": "Ponto final", "hostname": "Nome de anfitrião", @@ -1047,9 +1048,10 @@ "os": "Sistema Operacional", "processors": "Processadores", "reload_chosen_subsystems": "Recarregar Subsistemas Escolhidos", - "secrets": "Segredos do sistema", + "secrets": "Segredos", "secrets_create": "Criar Segredo", - "secrets_one": "Segredo do sistema", + "secrets_one": "Segredo", + "services": "Serviços", "start": "Começar", "subsystems": "Subsistemas", "success_reload": "Comando de recarga enviado com sucesso!", diff --git a/src/components/DataTables/DataTable/index.tsx b/src/components/DataTables/DataTable/index.tsx index cd20152..fcadbbd 100644 --- a/src/components/DataTables/DataTable/index.tsx +++ b/src/components/DataTables/DataTable/index.tsx @@ -53,6 +53,7 @@ export type DataTableProps = { obj?: string; sortBy?: { id: string; desc: boolean }[]; hiddenColumns?: string[]; + hideEmptyListText?: boolean; hideControls?: boolean; minHeight?: string | number; fullScreen?: boolean; @@ -77,6 +78,7 @@ const _DataTable = ({ sortBy, hiddenColumns, hideControls, + hideEmptyListText, count, setPageInfo, isManual, @@ -86,6 +88,7 @@ const _DataTable = ({ const { t } = useTranslation(); const breakpoint = useBreakpoint(); const textColor = useColorModeValue('gray.700', 'white'); + const hoveredRowBg = useColorModeValue('gray.100', 'gray.600'); const getPageSize = () => { try { if (showAllRows) return 1000000; @@ -142,6 +145,10 @@ const _DataTable = ({ usePagination, ) as TableInstanceWithHooks; + const handleGoToPage = (newPage: number) => { + if (saveSettingsId) localStorage.setItem(`${saveSettingsId}.page`, String(newPage)); + gotoPage(newPage); + }; const handleNextPage = () => { nextPage(); if (saveSettingsId) localStorage.setItem(`${saveSettingsId}.page`, String(pageIndex + 1)); @@ -256,7 +263,13 @@ const _DataTable = ({ {page.map((row: Row) => { prepareRow(row); return ( - + { // @ts-ignore row.cells.map((cell) => ( @@ -288,7 +301,7 @@ const _DataTable = ({ )} - {!isLoading && data.length === 0 && ( + {!isLoading && data.length === 0 && !hideEmptyListText && (
{obj ? ( @@ -309,7 +322,7 @@ const _DataTable = ({ gotoPage(0)} + onClick={() => handleGoToPage(0)} isDisabled={!canPreviousPage} icon={} mr={4} @@ -347,7 +360,7 @@ const _DataTable = ({ max={pageOptions.length} onChange={(_: unknown, numberValue: number) => { const newPage = numberValue ? numberValue - 1 : 0; - gotoPage(newPage); + handleGoToPage(newPage); }} defaultValue={pageIndex + 1} > @@ -386,7 +399,7 @@ const _DataTable = ({ gotoPage(pageCount - 1)} + onClick={() => handleGoToPage(pageCount - 1)} isDisabled={!canNextPage} icon={} ml={4} diff --git a/src/components/DataTables/SortableDataTable/index.tsx b/src/components/DataTables/SortableDataTable/index.tsx index 4179072..456fe13 100644 --- a/src/components/DataTables/SortableDataTable/index.tsx +++ b/src/components/DataTables/SortableDataTable/index.tsx @@ -100,6 +100,7 @@ const SortableDataTable: React.FC = ({ }) => { const { t } = useTranslation(); const breakpoint = useBreakpoint(); + const hoveredRowBg = useColorModeValue('gray.100', 'gray.600'); const textColor = useColorModeValue('gray.700', 'white'); const getPageSize = () => { const saved = saveSettingsId ? localStorage.getItem(saveSettingsId) : undefined; @@ -223,7 +224,13 @@ const SortableDataTable: React.FC = ({ {page.map((row: Row) => { prepareRow(row); return ( - + { // @ts-ignore row.cells.map((cell) => ( diff --git a/src/pages/SystemPage/SystemSecrets/Actions.tsx b/src/pages/SystemPage/SystemSecrets/Actions.tsx new file mode 100644 index 0000000..9f6fd23 --- /dev/null +++ b/src/pages/SystemPage/SystemSecrets/Actions.tsx @@ -0,0 +1,139 @@ +import React from 'react'; +import { CopyIcon } from '@chakra-ui/icons'; +import { + IconButton, + Tooltip, + Popover, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverFooter, + PopoverHeader, + PopoverTrigger, + Center, + Box, + Button, + useDisclosure, + HStack, + Text, + useClipboard, +} from '@chakra-ui/react'; +import { Eye, Trash } from 'phosphor-react'; +import { useTranslation } from 'react-i18next'; +import EditSecretButton from './EditButton'; +import { Secret, useDeleteSystemSecret } from 'hooks/Network/Secrets'; + +interface Props { + secret: Secret; + isDisabled?: boolean; +} + +const SystemSecretActions = ({ secret, isDisabled }: Props) => { + const { t } = useTranslation(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const deleteSecret = useDeleteSystemSecret(); + const { hasCopied, onCopy } = useClipboard(secret.value); + + const handleDeleteClick = React.useCallback(() => { + deleteSecret.mutate(secret.key, { + onSuccess: () => { + onClose(); + }, + }); + }, []); + + return ( + + + + + + } + size="sm" + isDisabled={isDisabled} + /> + + + + + + + + {t('crud.delete')} {secret.key} + + + {t('crud.delete_confirm', { obj: t('system.secrets_one') })} + + +
+ + +
+
+
+
+ + } + onClick={onCopy} + size="sm" + colorScheme="teal" + mr={2} + /> + + + + + + + } size="sm" colorScheme="purple" /> + + + + + + + + {t('common.view')} {secret.key} + + } + onClick={onCopy} + size="xs" + colorScheme="teal" + ml={2} + /> + + + + +
+
{secret.value}
+
+
+
+
+
+
+ ); +}; + +export default SystemSecretActions; diff --git a/src/pages/SystemPage/SystemSecrets/CreateButton.tsx b/src/pages/SystemPage/SystemSecrets/CreateButton.tsx new file mode 100644 index 0000000..e420948 --- /dev/null +++ b/src/pages/SystemPage/SystemSecrets/CreateButton.tsx @@ -0,0 +1,118 @@ +import * as React from 'react'; +import { + Box, + FormControl, + FormErrorMessage, + FormLabel, + Input, + Textarea, + useDisclosure, + useToast, +} from '@chakra-ui/react'; +import { useTranslation } from 'react-i18next'; +import { CreateButton } from '../../../components/Buttons/CreateButton'; +import { SaveButton } from '../../../components/Buttons/SaveButton'; +import { Modal } from '../../../components/Modals/Modal'; +import { useCreateSystemSecret } from 'hooks/Network/Secrets'; +import { AxiosError } from 'models/Axios'; + +type FormValues = { + key: string; + value: string; +}; + +const DEFAULT_FORM_VALUES: FormValues = { + key: '', + value: '', +}; + +const SystemSecretCreateButton = () => { + const { t } = useTranslation(); + const toast = useToast(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const [form, setForm] = React.useState(DEFAULT_FORM_VALUES); + const [isNameChanged, setIsNameChanged] = React.useState(false); + const [isValueChanged, setIsValueChanged] = React.useState(false); + const create = useCreateSystemSecret(); + + const onKeyChange = (e: React.ChangeEvent) => { + setForm({ ...form, key: e.target.value }); + if (!isNameChanged) setIsNameChanged(true); + }; + const onValueChange = (e: React.ChangeEvent) => { + setForm({ ...form, value: e.target.value }); + if (!isValueChanged) setIsValueChanged(true); + }; + + const isNameError = form.key.length === 0; + const isValueError = form.value.length === 0; + + const onSubmit = () => { + create.mutate(form, { + onSuccess: () => { + toast({ + id: 'create-system-secret-success', + title: t('common.success'), + description: t('crud.success_update_obj', { + obj: t('system.secrets_one'), + }), + status: 'success', + duration: 5000, + isClosable: true, + position: 'top-right', + }); + onClose(); + }, + onError: (e) => { + toast({ + id: 'create-system-secret-error', + title: t('common.error'), + description: (e as AxiosError)?.response?.data?.ErrorDescription, + status: 'error', + duration: 5000, + isClosable: true, + position: 'top-right', + }); + }, + }); + }; + + const handleOpenClick = () => { + setIsNameChanged(false); + setIsValueChanged(false); + setForm(DEFAULT_FORM_VALUES); + onOpen(); + }; + + return ( + <> + + + } + options={{ + modalSize: 'sm', + }} + > + + + {t('common.name')} + + {t('form.required')} + + + {t('common.value')} +