From 38e419b1b267d8654fe11b9b440b6caa4ccb651e Mon Sep 17 00:00:00 2001 From: svetaStrech Date: Sat, 9 Dec 2023 13:39:33 +0200 Subject: [PATCH] RND-305-general-enhancements --- ui_src/src/App.js | 2 +- ui_src/src/assets/images/metricsClockIcon.svg | 9 +- ui_src/src/assets/images/metricsErrorIcon.svg | 9 +- ui_src/src/assets/images/metricsIcon.svg | 15 +- ui_src/src/assets/images/orderingIcon.svg | 1 + ui_src/src/components/button/index.js | 1 - ui_src/src/components/connectorError/index.js | 121 ++++ .../src/components/connectorError/style.scss | 15 + ui_src/src/components/connectorModal/index.js | 20 +- .../src/components/connectorModal/style.scss | 15 +- ui_src/src/components/sdkExample/index.js | 38 +- ui_src/src/components/sdkExample/style.scss | 5 +- ui_src/src/components/select/index.js | 1 + ui_src/src/components/select/style.scss | 8 + ui_src/src/components/selectCheckBox/index.js | 5 +- .../src/components/selectCheckBox/style.scss | 13 +- ui_src/src/components/sideBar/index.js | 61 +- ui_src/src/connectors/index.js | 4 +- ui_src/src/connectors/kafka.js | 138 +++-- ui_src/src/const/apiEndpoints.js | 2 + .../components/functionDetails/index.js | 29 + .../components/functionList/index.js | 27 +- .../components/poisonMessage/index.js | 2 +- .../components/generateTokenModal/index.js | 149 +++-- .../components/generateTokenModal/style.scss | 20 +- .../ProduceConsumList/index.js | 117 +++- .../ProduceConsumList/style.scss | 5 + .../components/functionData/index.js | 83 +-- .../components/functionData/style.scss | 49 ++ .../components/functionInformation/index.js | 71 +++ .../components/functionInformation/style.scss | 67 +++ .../components/functionsOverview/style.scss | 48 +- .../stationOverviewHeader/index.js | 96 ++-- .../stationOverviewHeader/style.scss | 25 +- .../stationsList/stationBoxOverview/index.js | 2 +- .../domain/users/createUserDetails/index.js | 524 +++++++++--------- .../domain/users/createUserDetails/style.scss | 78 ++- ui_src/src/domain/users/index.js | 123 ++-- 38 files changed, 1310 insertions(+), 688 deletions(-) create mode 100644 ui_src/src/assets/images/orderingIcon.svg create mode 100644 ui_src/src/components/connectorError/index.js create mode 100644 ui_src/src/components/connectorError/style.scss create mode 100644 ui_src/src/domain/stationOverview/stationObservabilty/components/functionInformation/index.js create mode 100644 ui_src/src/domain/stationOverview/stationObservabilty/components/functionInformation/style.scss diff --git a/ui_src/src/App.js b/ui_src/src/App.js index 535a7c91f..a5fa6c97b 100644 --- a/ui_src/src/App.js +++ b/ui_src/src/App.js @@ -38,7 +38,7 @@ import { handleRefreshTokenRequest, httpRequest } from './services/http'; import { ReactComponent as RedirectIcon } from './assets/images/redirectIcon.svg'; import { ReactComponent as SuccessIcon } from './assets/images/successIcon.svg'; import { ReactComponent as CloseIcon } from './assets/images/closeNotification.svg'; -import { showMessages, useGetAllowedActions } from './services/genericServices'; +import {showMessages, useGetAllowedActions} from './services/genericServices'; import StationOverview from './domain/stationOverview'; import { ReactComponent as ErrorIcon } from './assets/images/errorIcon.svg'; import MessageJourney from './domain/messageJourney'; diff --git a/ui_src/src/assets/images/metricsClockIcon.svg b/ui_src/src/assets/images/metricsClockIcon.svg index 608fe120b..adc57538b 100644 --- a/ui_src/src/assets/images/metricsClockIcon.svg +++ b/ui_src/src/assets/images/metricsClockIcon.svg @@ -1,6 +1,5 @@ - - - - - + + + + diff --git a/ui_src/src/assets/images/metricsErrorIcon.svg b/ui_src/src/assets/images/metricsErrorIcon.svg index 566b5bf38..72ea80742 100644 --- a/ui_src/src/assets/images/metricsErrorIcon.svg +++ b/ui_src/src/assets/images/metricsErrorIcon.svg @@ -1,5 +1,6 @@ - - - - + + + + + diff --git a/ui_src/src/assets/images/metricsIcon.svg b/ui_src/src/assets/images/metricsIcon.svg index b19359f91..7add57a19 100644 --- a/ui_src/src/assets/images/metricsIcon.svg +++ b/ui_src/src/assets/images/metricsIcon.svg @@ -1,8 +1,9 @@ - - - - - - - + + + + + + + + diff --git a/ui_src/src/assets/images/orderingIcon.svg b/ui_src/src/assets/images/orderingIcon.svg new file mode 100644 index 000000000..b308c6811 --- /dev/null +++ b/ui_src/src/assets/images/orderingIcon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui_src/src/components/button/index.js b/ui_src/src/components/button/index.js index 7cc84d54c..c11d62d31 100644 --- a/ui_src/src/components/button/index.js +++ b/ui_src/src/components/button/index.js @@ -49,7 +49,6 @@ const Button = ({ tooltip, tooltip_placement = 'bottom', isVisible = true, - }) => { const handleClick = (e) => { onClick(e); diff --git a/ui_src/src/components/connectorError/index.js b/ui_src/src/components/connectorError/index.js new file mode 100644 index 000000000..4c36a5aad --- /dev/null +++ b/ui_src/src/components/connectorError/index.js @@ -0,0 +1,121 @@ +// Copyright 2022-2023 The Memphis.dev Authors +// Licensed under the Memphis Business Source License 1.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// Changed License: [Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0), as published by the Apache Foundation. +// +// https://github.com/memphisdev/memphis/blob/master/LICENSE +// +// Additional Use Grant: You may make use of the Licensed Work (i) only as part of your own product or service, provided it is not a message broker or a message queue product or service; and (ii) provided that you do not use, provide, distribute, or make available the Licensed Work as a Service. +// A "Service" is a commercial offering, product, hosted, or managed service, that allows third parties (other than your own employees and contractors acting on your behalf) to access and/or use the Licensed Work or a substantial set of the features or functionality of the Licensed Work to third parties as a software-as-a-service, platform-as-a-service, infrastructure-as-a-service or other similar services that compete with Licensor products or services. + +import './style.scss'; + +import React, { useEffect, useState } from 'react'; +import Modal from '../modal'; +import Spinner from '../spinner'; +import { ApiEndpoints } from '../../const/apiEndpoints'; +import { httpRequest } from '../../services/http'; +import { parsingDate } from '../../services/valueConvertor'; +import OverflowTip from '../tooltip/overflowtip'; + +const logsColumns = [ + { + key: '1', + title: 'Message', + width: '300px' + }, + { + key: '2', + title: 'Created at', + width: '200px' + } +]; + +const ConnectorError = ({ open, clickOutside, connectorId }) => { + const [loading, setLoading] = useState(false); + const [logs, setLogs] = useState(null); + + useEffect(() => { + open && getConnectorErrors(); + }, [open]); + + const getConnectorErrors = async () => { + setLoading(true); + try { + const data = await httpRequest('GET', ApiEndpoints.GET_CONNECTOR_ERRORS, { + connector_id: connectorId + }); + setLogs(data?.logs); + } catch (error) { + } finally { + setLoading(false); + } + }; + + const purgeConnectorErrors = async () => { + setLoading(true); + try { + await httpRequest('POST', ApiEndpoints.PURGE_CONNECTOR_ERRORS, { + connector_id: connectorId + }); + setLogs(null); + } catch (error) { + } finally { + setLoading(false); + } + }; + + return ( + +
+
+
+ {logsColumns?.map((column, index) => { + return ( + + {column.title} + + ); + })} +
+
+ {loading && ( +
+ +
+ )} + {!loading && (!logs || logs?.length === 0) &&

There are no logs to display

} + {logs?.map((row, index) => { + return ( +
+ + {row?.message} + + + {parsingDate(row?.created_at)} + +
+ ); + })} +
+
+
+ + ); +}; + +export default ConnectorError; diff --git a/ui_src/src/components/connectorError/style.scss b/ui_src/src/components/connectorError/style.scss new file mode 100644 index 000000000..d287a07c3 --- /dev/null +++ b/ui_src/src/components/connectorError/style.scss @@ -0,0 +1,15 @@ +.connector-errors { + .loader { + width: 100%; + display: flex; + justify-content: center; + margin-top: 10px; + } + .no-logs { + font-family: 'InterMedium'; + font-size: 14px; + color: #84818a; + margin-top: 10px; + margin-left: 10px; + } +} diff --git a/ui_src/src/components/connectorModal/index.js b/ui_src/src/components/connectorModal/index.js index defb1ebfe..04a29153e 100644 --- a/ui_src/src/components/connectorModal/index.js +++ b/ui_src/src/components/connectorModal/index.js @@ -36,7 +36,7 @@ const ConnectorModal = ({ open, clickOutside, newConnecor, source }) => { const [isEditing, setIsEditing] = useState(false); const [loading, setLoading] = useState(false); const [formFields, setFormFields] = useState({ - connector_type: source ? 'source' : 'sink' + connector_type: source ? 'Source' : 'Sink' }); const [connectorInputFields, setConnectorInputFields] = useState([]); const [resError, setError] = useState(null); @@ -46,7 +46,7 @@ const ConnectorModal = ({ open, clickOutside, newConnecor, source }) => { if (open) { setStep(1); setFormFields({ - connector_type: source ? 'source' : 'sink' + connector_type: source ? 'Source' : 'Sink' }); setIsEditing(false); setLoading(false); @@ -90,7 +90,11 @@ const ConnectorModal = ({ open, clickOutside, newConnecor, source }) => { const connectorModalTitle = step === 1 ? ( <> -

Add a new connector

+
+
+ Add a new connector Alpha +
+
) : loading ? ( @@ -138,8 +142,8 @@ const ConnectorModal = ({ open, clickOutside, newConnecor, source }) => { let data = await httpRequest('POST', ApiEndpoints.CREATE_CONNECTOR, { name: formFields?.name, station_id: stationState?.stationMetaData?.id, - type: formFields?.type, - connector_type: formFields?.connector_type, + type: formFields?.type?.toLocaleLowerCase(), + connector_type: formFields?.connector_type?.toLocaleLowerCase(), settings: modifiedSettings, partitions: [stationState?.stationPartition] }); @@ -164,7 +168,7 @@ const ConnectorModal = ({ open, clickOutside, newConnecor, source }) => { } ]} > - + {input?.type === 'string' && ( { return ( +
@@ -287,7 +291,7 @@ const ConnectorModal = ({ open, clickOutside, newConnecor, source }) => { radiusType="semi-round" height="40px" popupClassName="select-options" - options={['source', 'sink']} + options={['Source', 'Sink']} value={formFields?.connector_type} onChange={(e) => updateFormState('connector_type', e)} onBlur={(e) => updateFormState('connector_type', e)} diff --git a/ui_src/src/components/connectorModal/style.scss b/ui_src/src/components/connectorModal/style.scss index ba1fac131..796069363 100644 --- a/ui_src/src/components/connectorModal/style.scss +++ b/ui_src/src/components/connectorModal/style.scss @@ -12,11 +12,12 @@ margin-bottom: 10px; } .header-wrapper { - p { + .modal-title { font-family: 'InterSemiBold'; font-size: 16px; margin: 0; } + span { font-family: 'Inter'; font-size: 12px; @@ -54,4 +55,16 @@ white-space: pre-wrap; overflow-y: auto; } + .connector-modal-title { + display: flex; + align-items: center; + gap: 10px; + } +} +.connector-modal-header { + .modal-title { + font-family: 'InterMedium'; + font-size: 20px; + margin: 0; + } } diff --git a/ui_src/src/components/sdkExample/index.js b/ui_src/src/components/sdkExample/index.js index e8a5c1e37..7ee004aa4 100644 --- a/ui_src/src/components/sdkExample/index.js +++ b/ui_src/src/components/sdkExample/index.js @@ -29,7 +29,6 @@ import { } from '../../const/localStorageConsts'; import GenerateTokenModal from '../../domain/stationOverview/components/generateTokenModal'; import { ReactComponent as NoCodeExampleIcon } from '../../assets/images/noCodeExample.svg'; -import { ReactComponent as AddUserIcon } from '../../assets/images/addUserIcon.svg'; import { ReactComponent as RefreshIcon } from '../../assets/images/refresh.svg'; import { ReactComponent as CodeIcon } from '../../assets/images/codeIcon.svg'; import CreateUserDetails from '../../domain/users/createUserDetails'; @@ -44,6 +43,7 @@ import Switcher from '../switcher'; import Modal from '../modal'; import Input from '../Input'; import Copy from '../copy'; +import { Drawer } from 'antd'; loader.init(); loader.config({ monaco }); @@ -472,7 +472,7 @@ const SdkExample = ({ consumer, showTabs = true, stationName, username, connecti
-

Code generator

+

Client generator

)} @@ -861,33 +861,16 @@ const SdkExample = ({ consumer, showTabs = true, stationName, username, connecti returnToken={(e) => updateFormFields('jwt', e.jwt)} />
- -
- -
-

Add a new user

- - - } - height="470px" - width="450px" - rBtnText="Create" - lBtnText="Cancel" - lBtnClick={() => { - addUserModalFlip(false); - setCreateUserLoader(false); - }} - clickOutside={() => { + + { setCreateUserLoader(false); addUserModalFlip(false); }} - rBtnClick={() => { - setCreateUserLoader(true); - createUserRef.current(); - }} - isLoading={createUserLoader} + destroyOnClose={true} + width="650px" open={addUserModalIsOpen} > handleAddUser(userData)} handleLoader={(e) => setCreateUserLoader(e)} + isLoading={createUserLoader} /> -
+
diff --git a/ui_src/src/components/sdkExample/style.scss b/ui_src/src/components/sdkExample/style.scss index a71a3767b..b312b1970 100644 --- a/ui_src/src/components/sdkExample/style.scss +++ b/ui_src/src/components/sdkExample/style.scss @@ -136,9 +136,12 @@ width: 25px; } } + .modal-title-sdk { + font-family: 'InterMedium'; + font-size: 20px; + } p { margin-bottom: 0; - font-family: 'InterSemiBold'; } label { font-family: 'Inter'; diff --git a/ui_src/src/components/select/index.js b/ui_src/src/components/select/index.js index 4c109ed70..af3b712a5 100644 --- a/ui_src/src/components/select/index.js +++ b/ui_src/src/components/select/index.js @@ -85,6 +85,7 @@ const SelectComponent = ({ {option?.name} {option?.comment && {option?.comment}} + {option?.soon && Coming soon} ))} diff --git a/ui_src/src/components/select/style.scss b/ui_src/src/components/select/style.scss index 7818cc3fe..0d5c3b035 100644 --- a/ui_src/src/components/select/style.scss +++ b/ui_src/src/components/select/style.scss @@ -149,3 +149,11 @@ .ant-select-multiple .ant-select-selector { overflow-y: auto !important; } +.coming-soon-select { + color: #f4f4f4; + background-color: var(--purple); + padding: 2px 8px; + border-radius: 32px; + font-size: 10px; + font-family: 'InterSemiBold'; +} diff --git a/ui_src/src/components/selectCheckBox/index.js b/ui_src/src/components/selectCheckBox/index.js index e4c195e30..210e0de2e 100644 --- a/ui_src/src/components/selectCheckBox/index.js +++ b/ui_src/src/components/selectCheckBox/index.js @@ -15,9 +15,9 @@ import './style.scss'; import CheckCircleIcon from '@material-ui/icons/CheckCircle'; import React from 'react'; -const SelectCheckBox = ({ selectOptions, selectedOption, allowEdit = true, handleOnClick, button, hideCircle }) => { +const SelectCheckBox = ({ selectOptions, selectedOption, allowEdit = true, handleOnClick, button, hideCircle, vertical }) => { return ( -
+
{selectOptions.map((value) => { return (
handleOnClick(value)} + style={{ width: vertical && '100%' }} >
diff --git a/ui_src/src/components/selectCheckBox/style.scss b/ui_src/src/components/selectCheckBox/style.scss index 08e7d8b65..f8eedaed7 100644 --- a/ui_src/src/components/selectCheckBox/style.scss +++ b/ui_src/src/components/selectCheckBox/style.scss @@ -1,5 +1,4 @@ -.selecte-check-box-wrapper{ - +.selecte-check-box-wrapper { .option-wrapper { display: flex; flex-direction: row; @@ -11,7 +10,7 @@ font-family: 'InterMedium'; font-size: 16px; margin-bottom: 12px; - justify-content:space-between; + justify-content: space-between; svg { width: 18px; height: 18px; @@ -42,11 +41,11 @@ } } } - .check-and-content{ + .check-and-content { display: flex; width: 100%; justify-content: space-between; - .check-button{ + .check-button { display: flex; flex-direction: column; align-items: center; @@ -57,7 +56,7 @@ cursor: pointer; border-color: var(--purple) !important; } - .not-allowed{ + .not-allowed { cursor: not-allowed !important; background-color: #f5f5f5; } @@ -73,4 +72,4 @@ } } } -} \ No newline at end of file +} diff --git a/ui_src/src/components/sideBar/index.js b/ui_src/src/components/sideBar/index.js index e428e21ea..9e46d1c95 100644 --- a/ui_src/src/components/sideBar/index.js +++ b/ui_src/src/components/sideBar/index.js @@ -18,7 +18,7 @@ import ExitToAppOutlined from '@material-ui/icons/ExitToAppOutlined'; import PersonOutlinedIcon from '@material-ui/icons/PersonOutlined'; import { BsFillChatSquareTextFill } from 'react-icons/bs'; import { useHistory } from 'react-router-dom'; -import { Divider, Popover } from 'antd'; +import { Divider, Popover, Drawer } from 'antd'; import CloudMoadl from '../cloudModal'; import { LOCAL_STORAGE_ACCOUNT_NAME, @@ -48,8 +48,6 @@ import { ReactComponent as NewUserIcon } from '../../assets/images/newUserIcon.s import { ReactComponent as NewIntegrationIcon } from '../../assets/images/newIntegrationIcon.svg'; import { BsHouseHeartFill } from 'react-icons/bs'; import { ReactComponent as EditIcon } from '../../assets/images/editIcon.svg'; -import { ReactComponent as QuickActionBtn } from '../../assets/images/quickActionBtn.svg'; -import { ReactComponent as AddUserIcon } from '../../assets/images/addUserIcon.svg'; import { GithubRequest } from '../../services/githubRequests'; import { ReactComponent as LogsActiveIcon } from '../../assets/images/logsActive.svg'; import { ReactComponent as SchemaIcon } from '../../assets/images/schemaIcon.svg'; @@ -333,6 +331,14 @@ function SideBar() { setPopoverOpenSetting(false); }} /> + } + name="Users" + onClick={() => { + history.replace(pathDomains.users); + setPopoverOpenSetting(false); + }} + /> {isCloud() && ( } @@ -532,15 +538,6 @@ function SideBar() { route="logs" /> )} - } - activeIcon={} - name="Users" - onClick={() => history.replace(pathDomains.users)} - onMouseEnter={() => setHoveredItem('users')} - onMouseLeave={() => setHoveredItem('')} - route="users" - /> createStationModalFlip(false)} /> - -
- -
-

Add a new user

- -
- } - width="450px" - rBtnText="Create" - lBtnText="Cancel" - lBtnClick={() => { - addUserModalFlip(false); - setCreateUserLoader(false); - }} - clickOutside={() => { + { setCreateUserLoader(false); addUserModalFlip(false); }} - rBtnClick={() => { - setCreateUserLoader(true); - createUserRef.current(); - }} - isLoading={createUserLoader} + destroyOnClose={true} + width="650px" open={addUserModalIsOpen} > - handleAddUser(userData)} handleLoader={(e) => setCreateUserLoader(e)} /> - + { + handleAddUser(userData); + }} + handleLoader={(e) => setCreateUserLoader(e)} + isLoading={createUserLoader} + /> +
); } diff --git a/ui_src/src/connectors/index.js b/ui_src/src/connectors/index.js index e30de359b..0e11803fc 100644 --- a/ui_src/src/connectors/index.js +++ b/ui_src/src/connectors/index.js @@ -6,7 +6,7 @@ import { kafka } from './kafka'; import { kinesis } from './kinesis'; export const connectorTypes = [ - { name: 'kafka', icon: KafkaIcon, comment: 'Supported version: v1.0.3', inputs: kafka }, + { name: 'Kafka', icon: KafkaIcon, comment: 'Supported version: v1.0.3', inputs: kafka }, // { name: 'kinesis', icon: KinesisIcon, inputs: kinesis }, - { name: 's3', icon: S3LogoIcon, disabled: true } + { name: 'S3', icon: S3LogoIcon, disabled: true, soon: true } ]; diff --git a/ui_src/src/connectors/kafka.js b/ui_src/src/connectors/kafka.js index 5271ecef6..dfc031c8b 100644 --- a/ui_src/src/connectors/kafka.js +++ b/ui_src/src/connectors/kafka.js @@ -1,14 +1,14 @@ export const kafka = { - source: [ + Source: [ { name: 'name', - display: 'name', + display: 'Name', type: 'string', required: true }, { name: 'bootstrap.servers', - display: 'bootstrap.servers', + display: 'Bootstrap servers', type: 'multi', options: [], required: true, @@ -17,7 +17,7 @@ export const kafka = { }, { name: 'security.protocol', - display: 'security.protocol', + display: 'Security protocol', type: 'select', options: ['SSL', 'SASL_SSL', 'No authentication'], required: true, @@ -26,19 +26,19 @@ export const kafka = { SSL: [ { name: 'ssl.mechanism', - display: 'ssl.mechanism', + display: 'SSL mechanism', type: 'string', required: true }, { name: 'ssl.certificate.pem', - display: 'ssl.certificate.pem', + display: 'SSL certificate pem', type: 'string', required: true }, { name: 'ssl.key.password', - display: 'ssl.key.password', + display: 'SSL key password', type: 'string', required: true } @@ -46,20 +46,20 @@ export const kafka = { SASL_SSL: [ { name: 'sasl.mechanism', - display: 'sasl.mechanism', + display: 'SASL mechanism', type: 'select', options: ['PLAIN', 'SCRAM-SHA-256'], required: true }, { name: 'sasl.username', - display: 'sasl.username', + display: 'SASL username', type: 'string', required: true }, { name: 'sasl.password', - display: 'sasl.password', + display: 'SASL password', type: 'string', required: true } @@ -68,43 +68,73 @@ export const kafka = { }, { name: 'group.id', - display: 'group.id', + display: 'Group id', type: 'string', required: true, description: 'consumer group id' }, { - name: 'offset', - display: 'offset', - type: 'string', + name: 'offset_strategy', + display: 'Offset strategy', + type: 'select', + options: ['Earliest', 'End', 'Specific offset (int)'], required: false, - description: 'earliest / end / specific offset (int)' + description: 'choose offset strategy', + children: true, + Earliest: [], + End: [], + 'Specific offset (int)': [ + { + name: 'offset_value', + display: 'Value', + type: 'string', + required: true + } + ] }, { name: 'topic', - display: 'topic', + display: 'Topic', type: 'string', required: true, description: 'topic name' }, { - name: 'partition', - display: 'partition', + name: 'partition_strategy', + display: 'Partition strategy', + type: 'select', + options: ['Partition Number', 'Any Partition'], + required: true, + description: 'Partition Number / Any Partition', + children: true, + 'Partition Number': [ + { + name: 'partition_value', + display: 'Value', + type: 'string', + required: true + } + ], + 'Any Partition': [] + }, + { + name: 'timeout_duration_seconds', + display: 'kafka consumer timeout duration', type: 'string', required: false, - description: 'partition number' + description: 'kafka consumer timeout duration' } ], - sink: [ + Sink: [ { name: 'name', - display: 'name', + display: 'Name', type: 'string', required: true }, { name: 'bootstrap.servers', - display: 'bootstrap.servers', + display: 'Bootstrap servers', type: 'multi', options: [], required: true, @@ -113,7 +143,7 @@ export const kafka = { }, { name: 'security.protocol', - display: 'security.protocol', + display: 'Security protocol', type: 'select', options: ['SSL', 'SASL_SSL', 'No authentication'], required: true, @@ -122,19 +152,19 @@ export const kafka = { SSL: [ { name: 'ssl.mechanism', - display: 'ssl.mechanism', + display: 'SSL mechanism', type: 'string', required: true }, { name: 'ssl.certificate.pem', - display: 'ssl.certificate.pem', + display: 'SSL certificate pem', type: 'string', required: true }, { name: 'ssl.key.password', - display: 'ssl.key.password', + display: 'SSL key password', type: 'string', required: true } @@ -142,46 +172,72 @@ export const kafka = { SASL_SSL: [ { name: 'sasl.mechanism', - display: 'sasl.mechanism', + display: 'SASL mechanism', type: 'select', - options: ['PLAIN', 'SCRAM-SHA-256'], + options: ['PLAIN', 'SCRAM-SHA-256', 'SCRAM-SHA-512'], required: true }, { name: 'sasl.username', - display: 'sasl.username', + display: 'SASL username', type: 'string', required: true }, { name: 'sasl.password', - display: 'sasl.password', + display: 'SASL password', type: 'string', required: true } ], 'No authentication': [] }, - { - name: 'offset', - display: 'offset', - type: 'string', - required: false, - description: 'earliest / end / specific offset (int)' - }, { name: 'topic', - display: 'topic', + display: 'Topic', type: 'string', required: true, description: 'topic name' }, { - name: 'partition', - display: 'partition', + name: 'partition_strategy', + display: 'Partition strategy', + type: 'select', + options: ['Partition Key', 'Partition Number', 'Any Partition'], + required: true, + description: 'Partition Key / Partition Number / Any Partition', + children: true, + 'Partition Key': [ + { + name: 'partition_value', + display: 'Value', + type: 'string', + required: true + } + ], + 'Partition Number': [ + { + name: 'partition_value', + display: 'Value', + type: 'string', + required: true + } + ], + 'Any Partition': [] + }, + { + name: 'memphis_batch_size', + display: 'Memphis batch size', + type: 'string', + required: false, + description: 'memphis consuemr batch size' + }, + { + name: 'memphis_max_time_wait', + display: 'Max time to wait for a batch of messages', type: 'string', required: false, - description: 'partition number' + description: 'the time to wait for a batch of messages' } ] }; diff --git a/ui_src/src/const/apiEndpoints.js b/ui_src/src/const/apiEndpoints.js index 0e67919aa..5b22d47e4 100644 --- a/ui_src/src/const/apiEndpoints.js +++ b/ui_src/src/const/apiEndpoints.js @@ -57,6 +57,8 @@ export const ApiEndpoints = { REMOVE_CONNECTOR: '/stations/removeConnector', START_CONNECTOR: '/stations/startConnector', STOP_CONNECTOR: '/stations/stopConnector', + GET_CONNECTOR_ERRORS: '/stations/getConnectorErrors', + PURGE_CONNECTOR_ERRORS: '/stations/purgeConnectorErrors', //Async Tasks GET_ASYNC_TASKS: '/asyncTasks/getAsyncTasks', diff --git a/ui_src/src/domain/functions/components/functionDetails/index.js b/ui_src/src/domain/functions/components/functionDetails/index.js index f0a4d104a..e0bd5fdb7 100644 --- a/ui_src/src/domain/functions/components/functionDetails/index.js +++ b/ui_src/src/domain/functions/components/functionDetails/index.js @@ -335,6 +335,35 @@ function FunctionDetails({ selectedFunction, handleInstall, handleUnInstall, cli disabled={metaData?.installed_in_progress || !metaData?.installed} />
+ {!metaData?.installed ? ( + + +
Serverless functions to process ingested events "on the fly"
diff --git a/ui_src/src/domain/messageJourney/components/poisonMessage/index.js b/ui_src/src/domain/messageJourney/components/poisonMessage/index.js index 3962cdb7c..bfae883d2 100644 --- a/ui_src/src/domain/messageJourney/components/poisonMessage/index.js +++ b/ui_src/src/domain/messageJourney/components/poisonMessage/index.js @@ -33,7 +33,7 @@ const PoisonMessage = ({ stationName, messageId, details, message, headers, proc returnBack(); messageAnt.success({ key: 'memphisSuccessMessage', - content: 'The message was drop successfully', + content: 'The message was dropped successfully', duration: 5, style: { cursor: 'pointer' }, onClick: () => message.destroy('memphisSuccessMessage') diff --git a/ui_src/src/domain/stationOverview/components/generateTokenModal/index.js b/ui_src/src/domain/stationOverview/components/generateTokenModal/index.js index 0d2962d69..70c72b48a 100644 --- a/ui_src/src/domain/stationOverview/components/generateTokenModal/index.js +++ b/ui_src/src/domain/stationOverview/components/generateTokenModal/index.js @@ -12,19 +12,21 @@ import './style.scss'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { ApiEndpoints } from '../../../../const/apiEndpoints'; -import SelectComponent from '../../../../components/select'; +import CustomSelect from '../../../../components/customSelect'; import { ReactComponent as RefreshIcon } from '../../../../assets/images/refresh.svg'; import { httpRequest } from '../../../../services/http'; import Button from '../../../../components/button'; import Input from '../../../../components/Input'; import Copy from '../../../../components/copy'; +import CreateUserDetails from '../../../users/createUserDetails'; +import { Drawer } from 'antd'; import { LOCAL_STORAGE_ACCOUNT_ID, LOCAL_STORAGE_USER_PASS_BASED_AUTH } from '../../../../const/localStorageConsts'; import { isCloud } from '../../../../services/valueConvertor'; -const GenerateTokenModal = ({ host, close, returnToken }) => { +const GenerateTokenModal = ({ host, close, returnToken, restProducer, stationName }) => { const [isLoading, setIsLoading] = useState(true); const [generateLoading, setGenerateLoading] = useState(false); const [appUsers, setAppUsers] = useState([]); @@ -32,12 +34,15 @@ const GenerateTokenModal = ({ host, close, returnToken }) => { username: appUsers[0]?.name || '', connection_token: '', account_id: isCloud() ? Number(localStorage.getItem(LOCAL_STORAGE_ACCOUNT_ID)) : 1, - token_expiry_in_minutes: 123, + token_expiry_in_minutes: restProducer ? 525600 : 123, refresh_token_expiry_in_minutes: 10000092 }); const [userToken, setUserToken] = useState({}); const [tokenTitle, setTokenTitle] = useState('Connection token'); const [tokenPlaceHolder, setTokenPlaceHolder] = useState('Generated during user creation'); + const [addUserModalFlip, setAddUserModalFlip] = useState(false); + const [createUserLoader, setCreateUserLoader] = useState(false); + const createUserRef = useRef(null); const updateState = (field, value) => { let updatedValue = { ...formFields }; @@ -60,6 +65,13 @@ const GenerateTokenModal = ({ host, close, returnToken }) => { } }; + const handleAddUser = async (userData) => { + await getAppUsers(); + setCreateUserLoader(false); + setAddUserModalFlip(false); + updateState('username', userData.username); + }; + useEffect(() => { getAppUsers(); if (localStorage.getItem(LOCAL_STORAGE_USER_PASS_BASED_AUTH) === 'true') { @@ -67,7 +79,7 @@ const GenerateTokenModal = ({ host, close, returnToken }) => { username: appUsers[0]?.name || '', password: '', account_id: isCloud() ? Number(localStorage.getItem(LOCAL_STORAGE_ACCOUNT_ID)) : 1, - token_expiry_in_minutes: 123, + token_expiry_in_minutes: restProducer ? 525600 : 123, refresh_token_expiry_in_minutes: 10000092 }); setTokenTitle('Password'); @@ -97,17 +109,24 @@ const GenerateTokenModal = ({ host, close, returnToken }) => {
{!isLoading && ( <> -

- JWT token can be generated using a REST call, but for better convenience, it can also be generated through the GUI. -

By default, tokens are generated with 15-minutes expiration time for security purposes and can be refreshed using the "refresh - token" -

- {Object.keys(userToken).length === 0 ? ( + {restProducer ? ( +

+ Produce and consume data using the REST protocol. Great for webhooks ingestion and connecting your Memphis with other platforms +

+ ) : ( +

+ JWT token can be generated using a REST call, but for better convenience, it can also be generated through the GUI. +
+
+ By default, tokens are generated with 15-minutes expiration time for security purposes and can be refreshed using the "refresh token" +

+ )} + {((Object.keys(userToken).length === 0 && !restProducer) || restProducer) && ( <>

Client-type user

- { options={appUsers} value={formFields?.username || appUsers[0]} onChange={(e) => updateState('username', e)} + /> */} + updateState('username', e)} + type="user" + handleCreateNew={() => setAddUserModalFlip(true)} />
@@ -149,21 +176,9 @@ const GenerateTokenModal = ({ host, close, returnToken }) => { />
-
+

JWT refresh token

@@ -205,23 +221,78 @@ const GenerateTokenModal = ({ host, close, returnToken }) => { Generate again
-
); }; diff --git a/ui_src/src/domain/stationOverview/components/generateTokenModal/style.scss b/ui_src/src/domain/stationOverview/components/generateTokenModal/style.scss index 03242de08..146f1564c 100644 --- a/ui_src/src/domain/stationOverview/components/generateTokenModal/style.scss +++ b/ui_src/src/domain/stationOverview/components/generateTokenModal/style.scss @@ -7,7 +7,6 @@ .user-password-section { display: flex; flex-direction: column; - height: 240px; gap: 10px; } p { @@ -45,4 +44,23 @@ word-break: break-all !important; } } + .url-span { + color: rgb(29, 29, 29); + background-color: transparent; + box-shadow: none; + border: rgb(216, 216, 216) 1px solid; + border-radius: 5px; + height: 40px; + font-size: 14px; + width: 98%; + overflow-x: auto; + overflow-y: hidden; + display: flex; + align-items: center; + white-space: nowrap; + padding: 0px 15px; + } + .button-container { + margin-top: 20px; + } } diff --git a/ui_src/src/domain/stationOverview/stationObservabilty/ProduceConsumList/index.js b/ui_src/src/domain/stationOverview/stationObservabilty/ProduceConsumList/index.js index fffd33870..631900383 100644 --- a/ui_src/src/domain/stationOverview/stationObservabilty/ProduceConsumList/index.js +++ b/ui_src/src/domain/stationOverview/stationObservabilty/ProduceConsumList/index.js @@ -13,6 +13,7 @@ import './style.scss'; import React, { useContext, useEffect, useRef, useState } from 'react'; +import { LOCAL_STORAGE_ENV, LOCAL_STORAGE_REST_GW_HOST, LOCAL_STORAGE_REST_GW_PORT } from '../../../../const/localStorageConsts'; import { Space, Popover } from 'antd'; import { Virtuoso } from 'react-virtuoso'; import { FiPlayCircle } from 'react-icons/fi'; @@ -22,8 +23,7 @@ import { ReactComponent as PlayVideoIcon } from '../../../../assets/images/playV import { ReactComponent as PurplePlus } from '../../../../assets/images/purplePlus.svg'; import { ReactComponent as ProducerIcon } from '../../../../assets/images/producerIcon.svg'; import { ReactComponent as ConnectIcon } from '../../../../assets/images/connectIcon.svg'; -import { IoPlayCircleOutline, IoRemoveCircleOutline, IoPause } from 'react-icons/io5'; - +import { IoPlayCircleOutline, IoRemoveCircleOutline, IoPause, IoWarning } from 'react-icons/io5'; import { HiDotsVertical } from 'react-icons/hi'; import OverflowTip from '../../../../components/tooltip/overflowtip'; import { ReactComponent as UnsupportedIcon } from '../../../../assets/images/unsupported.svg'; @@ -32,16 +32,17 @@ import SdkExample from '../../../../components/sdkExample'; import CustomCollapse from '../components/customCollapse'; import Button from '../../../../components/button'; import Modal from '../../../../components/modal'; +import GenerateTokenModal from '../../../stationOverview/components/generateTokenModal'; import { StationStoreContext } from '../..'; import ProduceMessages from '../../../../components/produceMessages'; import ConnectorModal from '../../../../components/connectorModal'; +import ConnectorError from '../../../../components/connectorError'; import { ReactComponent as ErrorModalIcon } from '../../../../assets/images/errorModal.svg'; import { ApiEndpoints } from '../../../../const/apiEndpoints'; import { httpRequest } from '../../../../services/http'; -import { loader } from '@monaco-editor/react'; import Spinner from '../../../../components/spinner'; -import TooltipComponent from "../../../../components/tooltip/tooltip"; -import {isCloud} from "../../../../services/valueConvertor"; +import TooltipComponent from '../../../../components/tooltip/tooltip'; +import { isCloud } from '../../../../services/valueConvertor'; const overlayStylesConnectors = { borderRadius: '8px', @@ -53,12 +54,12 @@ const overlayStylesConnectors = { const overlayStyleConnectors = { borderRadius: '8px', width: '150px', paddingTop: '5px', paddingBottom: '5px' }; -const MenuItem = ({ name, onClick, icon, disabled, loader }) => { +const MenuItem = ({ name, onClick, icon, loader }) => { return ( -
+
{icon} - + {loader && }
@@ -76,6 +77,7 @@ const ProduceConsumList = ({ producer }) => { const [cgDetails, setCgDetails] = useState([]); const [openCreateProducer, setOpenCreateProducer] = useState(false); const [openCreateConsumer, setOpenCreateConsumer] = useState(false); + const [generateModal, setGenerateModal] = useState(false); const produceMessagesRef = useRef(null); const [produceloading, setProduceLoading] = useState(false); const [openNoConsumer, setOpenNoConsumer] = useState(false); @@ -85,6 +87,7 @@ const ProduceConsumList = ({ producer }) => { const [openConnectorPopoverItem, setOpenConnectorPopoverItem] = useState(null); const [selectedConnector, setSelectedConnector] = useState(null); const [openConnectorModal, setOpenConnectorModal] = useState(false); + const [openConnectorError, setOpenConnectorError] = useState(false); const [loading, setLoader] = useState(false); const producerItemsList = [ @@ -105,10 +108,9 @@ const ProduceConsumList = ({ producer }) => { { action: 'Produce using REST', onClick: () => { - // setOpenProduceMessages(true); + setGenerateModal(true); setOpenProducerPopover(false); - }, - disabled: true + } }, { action: 'Add a Source', @@ -119,6 +121,30 @@ const ProduceConsumList = ({ producer }) => { } ]; + const consumeItemsList = [ + { + action: 'Develop a Consumer', + onClick: () => { + setOpenCreateProducer(true); + setOpenProducerPopover(false); + } + }, + { + action: 'Consume using REST', + onClick: () => { + setGenerateModal(true); + setOpenProducerPopover(false); + } + }, + { + action: 'Add a Sink', + onClick: () => { + setOpenConnectorModal(true); + setOpenProducerPopover(false); + } + } + ]; + const removeConnector = async (type) => { setLoader(true); try { @@ -295,11 +321,15 @@ const ProduceConsumList = ({ producer }) => { } else return 'pubSub-row'; }; + const restGWHost = + localStorage.getItem(LOCAL_STORAGE_ENV) === 'docker' + ? `http://localhost:${localStorage.getItem(LOCAL_STORAGE_REST_GW_PORT)}` + : localStorage.getItem(LOCAL_STORAGE_REST_GW_HOST); const countProducers = (producersList) => { - const connected = producersList?.reduce((accumulator, item) => accumulator + item.connected_producers_count, 0) - const disconnected = producersList?.reduce((accumulator, item) => accumulator + item.disconnected_producers_count, 0) + const connected = producersList?.reduce((accumulator, item) => accumulator + item.connected_producers_count, 0); + const disconnected = producersList?.reduce((accumulator, item) => accumulator + item.disconnected_producers_count, 0); return connected + disconnected; - } + }; return (
@@ -310,9 +340,8 @@ const ProduceConsumList = ({ producer }) => {

<> - Sources - ({ producersList?.length > 0 && countProducers(producersList) || 0 } - { isCloud() && '/' + stationState?.stationSocketData?.max_amount_of_allowed_producers}) + Sources ({(producersList?.length > 0 && countProducers(producersList)) || 0} + {isCloud() && '/' + stationState?.stationSocketData?.max_amount_of_allowed_producers})

@@ -320,7 +349,7 @@ const ProduceConsumList = ({ producer }) => { overlayInnerStyle={overlayStylesConnectors} placement="bottomLeft" content={producerItemsList?.map((item, index) => ( - + ))} trigger="click" onOpenChange={() => setOpenProducerPopover(!openProducerPopover)} @@ -338,7 +367,7 @@ const ProduceConsumList = ({ producer }) => { ( + content={consumeItemsList?.map((item, index) => ( ))} trigger="click" @@ -386,16 +415,27 @@ const ProduceConsumList = ({ producer }) => { itemContent={(index, row) => (
onSelectedRow(index, 'producer')}> - {row?.connector_connection_id ? : } + {row?.connector_connection_id ? ( + + + + ) : ( + + + + )} {row.name} - -
- - {row.connected_producers_count + ' / ' + row.disconnected_producers_count} - +
+ {row?.connector_connection_id ? ( + '' + ) : ( + + {row.connected_producers_count + ' | ' + row.disconnected_producers_count} + + )}
@@ -412,7 +452,7 @@ const ProduceConsumList = ({ producer }) => { content={ <> (row?.is_active ? stopConnector('source') : startConnector('source'))} icon={row?.is_active ? : } /> @@ -422,6 +462,7 @@ const ProduceConsumList = ({ producer }) => { icon={} loader={loading && openConnectorPopoverItem === index} /> + setOpenConnectorError(true)} icon={} /> } trigger="click" @@ -444,7 +485,15 @@ const ProduceConsumList = ({ producer }) => { itemContent={(index, row) => (
onSelectedRow(index, 'consumer')}> - {row?.connector_connection_id ? : } + {row?.connector_connection_id ? ( + + + + ) : ( + + + + )} {row.name} @@ -474,11 +523,12 @@ const ProduceConsumList = ({ producer }) => { content={ <> (row?.is_active ? stopConnector('sink') : startConnector('sink'))} icon={row?.is_active ? : } /> removeConnector('sink')} icon={} /> + setOpenConnectorError(true)} icon={} /> } trigger="click" @@ -650,6 +700,17 @@ const ProduceConsumList = ({ producer }) => { newConnecor={(connector, source) => handleNewConnector(connector, source)} source={producer} /> + {`${producer ? 'Produce' : 'Consume'} using REST`}} + displayButtons={false} + width="460px" + clickOutside={() => setGenerateModal(false)} + open={generateModal} + className="generate-modal" + > + setGenerateModal(false)} /> + + setOpenConnectorError(false)} connectorId={selectedConnector?.id} />
); }; diff --git a/ui_src/src/domain/stationOverview/stationObservabilty/ProduceConsumList/style.scss b/ui_src/src/domain/stationOverview/stationObservabilty/ProduceConsumList/style.scss index 343a3faee..f5b3ea391 100644 --- a/ui_src/src/domain/stationOverview/stationObservabilty/ProduceConsumList/style.scss +++ b/ui_src/src/domain/stationOverview/stationObservabilty/ProduceConsumList/style.scss @@ -277,3 +277,8 @@ border-radius: 4px; color: var(--purple); } + +.rest-title { + font-family: 'InterMedium'; + font-size: 20px; +} diff --git a/ui_src/src/domain/stationOverview/stationObservabilty/components/functionData/index.js b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionData/index.js index 095623a0d..67ab41f3d 100644 --- a/ui_src/src/domain/stationOverview/stationObservabilty/components/functionData/index.js +++ b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionData/index.js @@ -20,9 +20,11 @@ import * as monaco from 'monaco-editor'; import { StationStoreContext } from '../../../'; import CustomTabs from '../../../../../components/Tabs'; import FunctionLogs from '../functionLogs'; +import FunctionInformation from '../functionInformation'; import { ReactComponent as MetricsIcon } from '../../../../../assets/images/metricsIcon.svg'; import { ReactComponent as MetricsClockIcon } from '../../../../../assets/images/metricsClockIcon.svg'; import { ReactComponent as MetricsErrorIcon } from '../../../../../assets/images/metricsErrorIcon.svg'; +import { ReactComponent as OrderingIcon } from '../../../../../assets/images/orderingIcon.svg'; import { ReactComponent as GitIcon } from '../../../../../assets/images/gitIcon.svg'; import { ReactComponent as CodeGrayIcon } from '../../../../../assets/images/codeGrayIcon.svg'; import { ReactComponent as PurpleQuestionMark } from '../../../../../assets/images/purpleQuestionMark.svg'; @@ -32,7 +34,7 @@ import { Drawer } from 'antd'; import { IoClose } from 'react-icons/io5'; import OverflowTip from '../../../../../components/tooltip/overflowtip'; -const tabValuesList = ['Information', 'Logs', 'Dead-letter']; +const tabValuesList = ['Information', 'Logs', 'Dead-letter', 'Monitoring']; loader.init(); loader.config({ monaco }); @@ -44,6 +46,12 @@ const FunctionData = ({ open, onClose, setOpenFunctionDetails, functionDetails } const [stationState, stationDispatch] = useContext(StationStoreContext); const [loadMessageData, setLoadMessageData] = useState(false); + useEffect(() => { + if (open) { + setTabValue('Information'); + } + }, [open]); + useEffect(() => { tabValue === tabValuesList[2] && getAttachedFunctionDlsMsgs(); }, [tabValue]); @@ -125,42 +133,7 @@ const FunctionData = ({ open, onClose, setOpenFunctionDetails, functionDetails } >
setTabValue(tabValue)} /> - {tabValue === tabValuesList[0] && ( -
-
-
- -
-
-
Total invocations
-
{functionDetails?.metrics?.total_invocations?.toLocaleString() || 0}
-
-
-
-
-
- -
-
-
Av. Processing time
-
- {functionDetails?.metrics?.average_processing_time} - /ms -
-
-
-
-
-
- -
-
-
Error rate
-
{functionDetails?.metrics?.error_rate}%
-
-
-
- )} + {tabValue === tabValuesList[0] && } {tabValue === tabValuesList[1] && } {tabValue === tabValuesList[2] && ( @@ -222,6 +195,42 @@ const FunctionData = ({ open, onClose, setOpenFunctionDetails, functionDetails } )} )} + {tabValue === tabValuesList[3] && ( +
+
+ +
+
Total invocations
+
{functionDetails?.metrics?.total_invocations?.toLocaleString() || 0}
+
+
+ +
+ +
+
Av. Processing time
+
+ {functionDetails?.metrics?.average_processing_time} + /ms +
+
+
+
+ +
+
Error rate
+
{functionDetails?.metrics?.error_rate}%
+
+
+
+ +
+
Ordering
+
{functionDetails?.ordering_matter ? 'Yes' : 'No'}
+
+
+
+ )}
); diff --git a/ui_src/src/domain/stationOverview/stationObservabilty/components/functionData/style.scss b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionData/style.scss index 348d9de70..cd5e2ace8 100644 --- a/ui_src/src/domain/stationOverview/stationObservabilty/components/functionData/style.scss +++ b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionData/style.scss @@ -1,4 +1,6 @@ .function-data-container { + position: relative; + height: calc(100% - 80px); .logs-container { white-space: pre; height: 150px; @@ -80,3 +82,50 @@ } } } +.metrics-wrapper { + display: flex; + gap: 20px; + .metrics { + height: 130px; + width: 200px; + padding-left: 20px; + display: flex; + flex-direction: column; + justify-content: center; + gap: 3px; + border-radius: 8px; + border: 1px solid #e6e6e6; + box-shadow: 0px 5px 8px -5px rgba(10, 9, 11, 0.04), 0px 1px 4px 1px rgba(10, 9, 11, 0.02); + &-body { + display: flex; + flex-direction: column; + margin-top: 10px; + gap: 5px; + &-title { + color: #b5b5b5; + font-family: Inter; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 24px; /* 200% */ + } + + &-subtitle { + color: #1d1d1d; + font-family: Inter; + font-size: 20.211px; + font-style: normal; + font-weight: 700; + line-height: normal; + } + .ms { + color: #b5b5b5; + font-family: Inter; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 24px; + } + } + } +} diff --git a/ui_src/src/domain/stationOverview/stationObservabilty/components/functionInformation/index.js b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionInformation/index.js new file mode 100644 index 000000000..fc8ddc4ce --- /dev/null +++ b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionInformation/index.js @@ -0,0 +1,71 @@ +// Copyright 2022-2023 The Memphis.dev Authors +// Licensed under the Memphis Business Source License 1.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// Changed License: [Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0), as published by the Apache Foundation. +// +// https://github.com/memphisdev/memphis/blob/master/LICENSE +// +// Additional Use Grant: You may make use of the Licensed Work (i) only as part of your own product or service, provided it is not a message broker or a message queue product or service; and (ii) provided that you do not use, provide, distribute, or make available the Licensed Work as a Service. +// A "Service" is a commercial offering, product, hosted, or managed service, that allows third parties (other than your own employees and contractors acting on your behalf) to access and/or use the Licensed Work or a substantial set of the features or functionality of the Licensed Work to third parties as a software-as-a-service, platform-as-a-service, infrastructure-as-a-service or other similar services that compete with Licensor products or services. + +import './style.scss'; + +import React from 'react'; +import OverflowTip from '../../../../../components/tooltip/overflowtip'; + +const inputsColumns = [ + { + key: '1', + title: 'Key', + width: '300px' + }, + { + key: '2', + title: 'Value', + width: '300px' + } +]; + +const FunctionInformation = ({ inputs }) => { + return ( +
+

Inputs

+
+
+
+ {inputsColumns?.map((column, index) => { + return ( + + {column.title} + + ); + })} +
+ {(!inputs || Object.entries(inputs)?.length === 0) && ( +
+

This function has no inputs

+
+ )} +
+ {Object.entries(inputs)?.map((key, index) => { + return ( +
+ + {key[0]} + + + {key[1]} + +
+ ); + })} +
+
+
+
+ ); +}; + +export default FunctionInformation; diff --git a/ui_src/src/domain/stationOverview/stationObservabilty/components/functionInformation/style.scss b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionInformation/style.scss new file mode 100644 index 000000000..8ff47163a --- /dev/null +++ b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionInformation/style.scss @@ -0,0 +1,67 @@ +.function-inputs-container { + display: flex; + flex-direction: column; + height: 100%; + .title { + font-family: 'InterSemiBold'; + font-size: 16px; + margin-bottom: 5px; + } + .generic-list-wrapper { + display: flex; + position: relative; + height: calc(100% - 40px); + width: 700px; + .list { + position: relative; + width: 100%; + .coulmns-table { + border-bottom: 1px solid #e9e9e9; + display: flex; + gap: 10px; + justify-content: flex-start; + padding-left: 22px; + span { + font-family: 'Inter'; + font-size: 12px; + margin-right: 10px; + } + } + .rows-wrapper { + position: relative; + height: calc(100% - 10px); + overflow: auto; + .pubSub-row { + width: 100%; + height: 35px; + display: flex; + color: #1d1d1d; + gap: 10px; + justify-content: flex-start; + align-items: center; + font-family: 'Inter'; + font-size: 12px; + padding-left: 22px; + div { + margin-right: 10px; + } + } + .pubSub-row:nth-child(even) { + background-color: #f8f8f8; + border-radius: 5px; + } + .no-inputs { + margin-top: 10px; + margin-left: 20px; + } + } + } + .row-data { + width: 18vw; + overflow-wrap: anywhere; + overflow: auto; + margin: 1vh; + margin-top: 45px; + } + } +} diff --git a/ui_src/src/domain/stationOverview/stationObservabilty/components/functionsOverview/style.scss b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionsOverview/style.scss index ba118cbff..b99a9c3a2 100644 --- a/ui_src/src/domain/stationOverview/stationObservabilty/components/functionsOverview/style.scss +++ b/ui_src/src/domain/stationOverview/stationObservabilty/components/functionsOverview/style.scss @@ -12,6 +12,8 @@ display: flex; flex-direction: column; justify-content: space-between; + align-items: center; + text-align: center; gap: 3px; border-right: 1px solid #f3f3f3; flex: 1; @@ -270,49 +272,3 @@ } } } -.metrics { - display: flex; - flex-direction: column; - gap: 14px; - - &-divider { - height: 130px; - background: #e3e3e3; - width: 1px; - } - - &-wrapper { - display: flex; - justify-content: center; - gap: 40px; - margin: 8px 0; - } - &-body { - &-title { - color: #b5b5b5; - font-family: Inter; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 24px; /* 200% */ - } - - &-subtitle { - color: #1d1d1d; - font-family: Inter; - font-size: 20.211px; - font-style: normal; - font-weight: 700; - line-height: normal; - - span { - color: #b0b0b0; - font-family: Inter; - font-size: 12.632px; - font-style: normal; - font-weight: 600; - line-height: normal; - } - } - } -} diff --git a/ui_src/src/domain/stationOverview/stationOverviewHeader/index.js b/ui_src/src/domain/stationOverview/stationOverviewHeader/index.js index c943f830c..f968ac117 100644 --- a/ui_src/src/domain/stationOverview/stationOverviewHeader/index.js +++ b/ui_src/src/domain/stationOverview/stationOverviewHeader/index.js @@ -34,6 +34,7 @@ import TooltipComponent from '../../../components/tooltip/tooltip'; import OverflowTip from '../../../components/tooltip/overflowtip'; import UpdateSchemaModal from '../components/updateSchemaModal'; import ActiveBadge from '../../../components/activeBadge'; +import Copy from '../../../components/copy'; import { ApiEndpoints } from '../../../const/apiEndpoints'; import { ReactComponent as BackIcon } from '../../../assets/images/backIcon.svg'; import UseSchemaModal from '../components/useSchemaModal'; @@ -46,7 +47,7 @@ import Auditing from '../components/auditing'; import AsyncTasks from '../../../components/asyncTasks'; import pathDomains from '../../../router'; import { StationStoreContext } from '..'; -import { TIERED_STORAGE_UPLOAD_INTERVAL } from '../../../const/localStorageConsts'; +import { TIERED_STORAGE_UPLOAD_INTERVAL, LOCAL_STORAGE_ACCOUNT_ID, LOCAL_STORAGE_ENV, LOCAL_STORAGE_BROKER_HOST } from '../../../const/localStorageConsts'; import { Context } from '../../../hooks/store'; const StationOverviewHeader = () => { @@ -67,6 +68,13 @@ const StationOverviewHeader = () => { const getAllowedActions = useGetAllowedActions(); const history = useHistory(); + let host = + localStorage.getItem(LOCAL_STORAGE_ENV) === 'docker' + ? 'localhost' + : localStorage.getItem(LOCAL_STORAGE_BROKER_HOST) + ? localStorage.getItem(LOCAL_STORAGE_BROKER_HOST) + : 'memphis.memphis.svc.cluster.local'; + const showRetentinViolation = isCloud() && stationState?.stationMetaData?.retention_type !== 'message_age_sec'; const dls = stationState?.stationMetaData?.dls_station === '' ? null : stationState?.stationMetaData?.dls_station; useEffect(() => { @@ -192,10 +200,22 @@ const StationOverviewHeader = () => { }} />
- +
Created by {stationState?.stationMetaData?.created_by_username} at {stationState?.stationMetaData?.created_at}{' '} {!stationState?.stationMetaData?.is_native && '(NATS-Compatible)'} - + {isCloud() && ( + +

Account ID :

+ {localStorage.getItem(LOCAL_STORAGE_ACCOUNT_ID)} + +
+ )} + +

Broker hostname :

+ {host} + +
+
{stationState?.stationMetaData?.partitions_number > 1 && ( @@ -245,15 +265,16 @@ const StationOverviewHeader = () => { {stationState?.stationMetaData?.partitions_number === 0 ? 1 : stationState?.stationMetaData?.partitions_number}

-

- Dead-letter for: - { - stationState?.stationSocketData?.act_as_dls_station_in_stations && stationState?.stationSocketData?.act_as_dls_station_in_stations.length ? +

+ Dead-letter for: + {stationState?.stationSocketData?.act_as_dls_station_in_stations && + stationState?.stationSocketData?.act_as_dls_station_in_stations.length ? ( {stationState?.stationSocketData?.act_as_dls_station_in_stations.join(', ')} - : - } + ) : ( + + )}

@@ -284,46 +305,43 @@ const StationOverviewHeader = () => {

Schema validation

{(!stationState?.stationSocketData?.schema?.name && !stationState?.stationSocketData?.schema?.schema_type) || - stationState?.stationSocketData?.schema !== undefined && - Object.keys(stationState?.stationSocketData?.schema).length !== 0 && ( -
- {stationState?.stationSocketData?.schema?.updates_available && - } -
- )} + (stationState?.stationSocketData?.schema !== undefined && Object.keys(stationState?.stationSocketData?.schema).length !== 0 && ( +
+ {stationState?.stationSocketData?.schema?.updates_available && } +
+ ))}
{(!stationState?.stationSocketData?.schema?.name && !stationState?.stationSocketData?.schema?.schema_type) || - stationState?.stationSocketData?.schema !== undefined && - Object.keys(stationState?.stationSocketData?.schema).length !== 0 && ( - history.push(`${pathDomains.schemaverse}/list/${stationState?.stationSocketData?.schema?.name}`)} - /> - )} + (stationState?.stationSocketData?.schema !== undefined && Object.keys(stationState?.stationSocketData?.schema).length !== 0 && ( + history.push(`${pathDomains.schemaverse}/list/${stationState?.stationSocketData?.schema?.name}`)} + /> + ))}
{(!stationState?.stationSocketData?.schema?.name && !stationState?.stationSocketData?.schema?.schema_type) || - stationState?.stationSocketData?.schema !== undefined && - Object.keys(stationState?.stationSocketData?.schema).length !== 0 && ( -
- - {stationState?.stationSocketData?.schema?.name} - - -

v{stationState?.stationSocketData?.schema?.version_number}

-
- )} - {((!stationState?.stationSocketData?.schema?.name && !stationState?.stationSocketData?.schema?.schema_type) || + (stationState?.stationSocketData?.schema !== undefined && Object.keys(stationState?.stationSocketData?.schema).length !== 0 && ( +
+ + {stationState?.stationSocketData?.schema?.name} + + +

v{stationState?.stationSocketData?.schema?.version_number}

+
+ ))} + {stationState?.stationSocketData?.schema && + ((!stationState?.stationSocketData?.schema?.name && !stationState?.stationSocketData?.schema?.schema_type) || Object.keys(stationState?.stationSocketData?.schema).length === 0 ? (
} tooltip={!stationState?.stationMetaData?.is_native && 'Supported only by using Memphis SDKs'} @@ -442,7 +460,7 @@ const StationOverviewHeader = () => {
-

Code generator

+

Client generator

{ setSdkModal(true); diff --git a/ui_src/src/domain/stationOverview/stationOverviewHeader/style.scss b/ui_src/src/domain/stationOverview/stationOverviewHeader/style.scss index 28d506558..878e57580 100644 --- a/ui_src/src/domain/stationOverview/stationOverviewHeader/style.scss +++ b/ui_src/src/domain/stationOverview/stationOverviewHeader/style.scss @@ -8,6 +8,7 @@ align-items: flex-start; height: 35px; .station-details { + width: 100%; .station-name { margin-bottom: 5px; margin-right: 5px; @@ -34,6 +35,28 @@ color: #979797; font-size: 12px; font-family: 'Inter'; + display: flex; + align-items: center; + gap: 10px; + } + .hostname { + border-radius: 32px; + border: 0.8px solid #e2dfff; + background: #f1f0ff; + display: flex; + width: fit-content; + padding: 0px 10px; + gap: 2px; + font-family: 'InterMedium'; + p { + font-size: 12px; + margin: 0; + } + span { + color: var(--purple); + font-size: 12px; + margin-right: 5px; + } } } .station-buttons { @@ -434,5 +457,5 @@ } .disable-consumption-modal .dls-name { - display: initial!important; + display: initial !important; } diff --git a/ui_src/src/domain/stationsList/stationBoxOverview/index.js b/ui_src/src/domain/stationsList/stationBoxOverview/index.js index 64c6366d2..d858b7e49 100644 --- a/ui_src/src/domain/stationsList/stationBoxOverview/index.js +++ b/ui_src/src/domain/stationsList/stationBoxOverview/index.js @@ -27,7 +27,7 @@ import { ReactComponent as ReplicasIcon } from '../../../assets/images/replicasI import { ReactComponent as TotalMsgIcon } from '../../../assets/images/totalMsgIcon.svg'; import { ReactComponent as PoisonMsgIcon } from '../../../assets/images/poisonMsgIcon.svg'; import { ReactComponent as RemoteStorageIcon } from '../../../assets/images/remoteStorage.svg'; -import { ReactComponent as ClockIcon } from '../../../assets/images/TimeFill.svg'; +import { ReactComponent as ClockIcon } from '../../../assets/images/timeFill.svg'; import { ReactComponent as UserIcon } from '../../../assets/images/userPerson.svg'; import { ReactComponent as SchemaIcon } from '../../../assets/images/schemaIconActive.svg'; import { ReactComponent as StationIcon } from '../../../assets/images/stationsIconActive.svg'; diff --git a/ui_src/src/domain/users/createUserDetails/index.js b/ui_src/src/domain/users/createUserDetails/index.js index 2b70a2d89..b60fc569b 100644 --- a/ui_src/src/domain/users/createUserDetails/index.js +++ b/ui_src/src/domain/users/createUserDetails/index.js @@ -17,17 +17,21 @@ import { Form } from 'antd'; import { HiLockClosed } from 'react-icons/hi'; import Input from '../../../components/Input'; import RadioButton from '../../../components/radioButton'; +import Button from '../../../components/button'; +import SelectComponent from '../../../components/select'; import { httpRequest } from '../../../services/http'; import { useGetAllowedActions } from '../../../services/genericServices'; import { ApiEndpoints } from '../../../const/apiEndpoints'; import SelectCheckBox from '../../../components/selectCheckBox'; import { generator } from '../../../services/generator'; +import { ReactComponent as RefreshIcon } from '../../../assets/images/refresh.svg'; + import { LOCAL_STORAGE_USER_PASS_BASED_AUTH } from '../../../const/localStorageConsts'; import { isCloud, showUpgradePlan } from '../../../services/valueConvertor'; import { Context } from '../../../hooks/store'; import UpgradePlans from '../../../components/upgradePlans'; -const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, clientType = false }) => { +const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, isLoading, clientType = false }) => { const [state, dispatch] = useContext(Context); const [creationForm] = Form.useForm(); const [formFields, setFormFields] = useState({ @@ -36,7 +40,6 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, }); const [userType, setUserType] = useState(clientType ? 'application' : 'management'); const [userViolation, setUserViolation] = useState(false); - const [passwordType, setPasswordType] = useState(0); const userTypeOptions = [ { id: 1, @@ -53,29 +56,12 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, disabled: false } ]; - const passwordOptions = [ - { - id: 1, - value: 0, - label: 'Auto-Generated' - }, - { - id: 2, - value: 1, - label: 'Custom' - } - ]; - const [generatedPassword, setGeneratedPassword] = useState(''); + const getAllowedActions = useGetAllowedActions(); useEffect(() => { createUserRef.current = onFinish; - generateNewPassword(); }, []); - const passwordTypeChange = (e) => { - setPasswordType(e.target.value); - }; - const updateFormState = (field, value) => { let updatedValue = { ...formFields }; updatedValue[field] = value; @@ -103,9 +89,6 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, handleLoader(false); return; } - if (fieldsValue?.passwordType === 0 ?? passwordType === 0) { - fieldsValue['password'] = fieldsValue['generatedPassword']; - } try { const bodyRequest = fieldsValue; const data = await httpRequest('POST', ApiEndpoints.ADD_USER, bodyRequest); @@ -125,8 +108,8 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, const generateNewPassword = () => { const newPassword = generator(); - setGeneratedPassword(newPassword); - creationForm.setFieldsValue({ ['generatedPassword']: newPassword }); + updateFormState('password', newPassword); + creationForm.setFieldsValue({ ['password']: newPassword }); }; const handleUserTypeChanged = (value) => { @@ -136,273 +119,288 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, return (
-
-
- - type.value === 'application') : userTypeOptions} - handleOnClick={(e) => handleUserTypeChanged(e.value)} - selectedOption={userType} - /> - -
-
-

User details

- -
-

{userType === 'management' && isCloud() ? 'Email*' : 'Username*'}

- updateFormState('username', e.target.value)} - onChange={(e) => updateFormState('username', e.target.value)} - value={formFields.name} + +
+
+ + type.value === 'application') : userTypeOptions} + handleOnClick={(e) => handleUserTypeChanged(e.value)} + selectedOption={userType} /> -
- - {userType === 'management' && ( - <> - -
-

{isCloud() ? 'Full name*' : 'Full name'}

- updateFormState('full_name', e.target.value)} - onChange={(e) => updateFormState('full_name', e.target.value)} - value={formFields.full_name} - /> + +
+
+

User details

+ +
+

{userType === 'management' && isCloud() ? 'Email*' : 'Username*'}

+ updateFormState('username', e.target.value)} + onChange={(e) => updateFormState('username', e.target.value)} + value={formFields.name} + /> +
+
+ {userType === 'management' && ( + <> + {userType === 'management' && ( + +
+

{isCloud() ? 'Full name*' : 'Full name'}

+ updateFormState('full_name', e.target.value)} + onChange={(e) => updateFormState('full_name', e.target.value)} + value={formFields.full_name} + /> +
+
+ )} +
+ +
+

Team

+ updateFormState('team', e.target.value)} + onChange={(e) => updateFormState('team', e.target.value)} + value={formFields.team} + /> +
+
+ +
+

Position

+ updateFormState('position', e.target.value)} + onChange={(e) => updateFormState('position', e.target.value)} + value={formFields.position} + /> +
+
- -
- -
-

Team

+ + )} + {userType === 'application' && ( + <> + +
+

Description

updateFormState('team', e.target.value)} - onChange={(e) => updateFormState('team', e.target.value)} - value={formFields.team} + onBlur={(e) => updateFormState('description', e.target.value)} + onChange={(e) => updateFormState('description', e.target.value)} + value={formFields.description} />
- -
-

Position

- updateFormState('position', e.target.value)} - onChange={(e) => updateFormState('position', e.target.value)} - value={formFields.position} - /> + + )} +
+ + {((userType === 'management' && !isCloud()) || + (userType === 'application' && localStorage.getItem(LOCAL_STORAGE_USER_PASS_BASED_AUTH) === 'true')) && ( +
+

Set password

+ +
+
+

Set password or generate one

+ + +

Generate password

+
- -
- - )} - {userType === 'application' && ( - <> - -
-

Description

updateFormState('description', e.target.value)} - onChange={(e) => updateFormState('description', e.target.value)} - value={formFields.description} + value={formFields?.password} + onChange={(e) => { + updateFormState('password', e.target.value); + // setGeneratedPassword(e.target.value); + // creationForm.setFieldsValue({ ['generatedPassword']: e.target.value }); + }} + onBlur={(e) => { + updateFormState('password', e.target.value); + }} />
- +
)} -
- - {((userType === 'management' && !isCloud()) || (userType === 'application' && localStorage.getItem(LOCAL_STORAGE_USER_PASS_BASED_AUTH) === 'true')) && ( -
-

Set password

- - passwordTypeChange(e)} + <> +
+ +

Roles

+ +
+ - - {passwordType === 0 && ( - -
-

New password

- -

generateNewPassword()}> - Generate again -

-
-
- )} - {passwordType === 1 && ( -
-
-

Type password*

- - - -
-
-

Confirm Password*

- ({ - validator(rule, value) { - if (!value || getFieldValue('password') === value) { - updateFormState('password', value); - return Promise.resolve(); - } - return Promise.reject('Passwords do not match'); - } - }) - ]} - > - - -
+ +

Permissions

+ +
+ + +

Tenants

+ +
+ +
+ + {userViolation && ( +
+
+ +

Your current plan allows {state?.userData?.entitlements['feature-management-users']?.limits} management users

- )} -
- )} - {userViolation && ( -
-
- -

Your current plan allows {state?.userData?.entitlements['feature-management-users']?.limits} management users

+ {showUpgradePlan() && ( + +

Upgrade now

+
+ } + isExternal={false} + /> + )}
- {showUpgradePlan() && ( - -

Upgrade now

-
- } - isExternal={false} - /> - )} -
- )} + )} +
+
); diff --git a/ui_src/src/domain/users/createUserDetails/style.scss b/ui_src/src/domain/users/createUserDetails/style.scss index a7e69417a..a53445408 100644 --- a/ui_src/src/domain/users/createUserDetails/style.scss +++ b/ui_src/src/domain/users/createUserDetails/style.scss @@ -1,37 +1,87 @@ .create-user-form { - p{ + height: 100%; + .user-form { + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; + .coming-soon-container { + display: flex; + align-items: center; + gap: 5px; + margin-bottom: 5px; + margin-top: 10px; + p { + margin: 0; + } + .coming-soon { + color: #f4f4f4; + background-color: var(--purple); + padding: 2px 8px; + border-radius: 32px; + font-size: 8px; + font-family: 'InterSemiBold'; + } + } + .button-container { + margin-top: 10px; + width: 100%; + display: flex; + justify-content: center; + } + } + .user-row { + display: flex; + gap: 10px; + div { + width: 100%; + } + } + p { margin-bottom: 5px; } .ant-form-item { margin-bottom: 0px; } - .fields-title{ + .fields-title { font-family: 'InterMedium'; font-size: 16px; } - .field-title{ - font-size:12px; + .field-title { + font-size: 12px; font-family: 'Inter'; font-weight: 400; + margin-top: 5px; } - .user-details{ - margin-bottom: 12px; - } - .field{ + .field { margin-bottom: 10px; } - .user-type{ + .user-type { margin-bottom: 0 !important; } .password { - .generate-password-button{ + .password-title { + width: 100%; + display: flex; + justify-content: space-between; + .generate-btn { + display: flex; + align-items: center; + cursor: pointer; + gap: 5px; + p { + margin: 0; + } + } + } + .generate-password-button { color: var(--purple); cursor: pointer; font-family: 'InterSemiBold'; font-size: 12px; } } - .flex-row{ + .flex-row { display: flex; justify-content: space-between; } @@ -44,7 +94,7 @@ text-align: center; margin-right: 5px; color: #808191; - border-color: rgba(243,243,243,1); + border-color: rgba(243, 243, 243, 1); content: normal; border-left-width: 1px; } @@ -68,7 +118,7 @@ } } } - .show-violation-form{ - margin-top: 0; + .show-violation-form { + margin-top: 0; } } diff --git a/ui_src/src/domain/users/index.js b/ui_src/src/domain/users/index.js index a61470765..36cd95b90 100644 --- a/ui_src/src/domain/users/index.js +++ b/ui_src/src/domain/users/index.js @@ -36,7 +36,8 @@ import Modal from '../../components/modal'; import Table from '../../components/table'; import DeleteItemsModal from '../../components/deleteItemsModal'; import CloudModal from '../../components/cloudModal'; -import {isCurrentUser} from "../../utils/user"; +import { isCurrentUser } from '../../utils/user'; +import { Drawer } from 'antd'; function Users() { const [state, dispatch] = useContext(Context); @@ -264,26 +265,27 @@ function Users() { key: 'action', render: (_, record) => (
- {(!isCurrentUser(record.id) && -
- } - colorType="red" - radiusType="circle" - border="gray-light" - backgroundColorType={'white'} - fontSize="12px" - fontFamily="InterMedium" - onClick={() => { - deleteUser(record.username, record.user_type); - }} - />)} + {!isCurrentUser(record.id) && ( +
+ } + colorType="red" + radiusType="circle" + border="gray-light" + backgroundColorType={'white'} + fontSize="12px" + fontFamily="InterMedium" + onClick={() => { + deleteUser(record.username, record.user_type); + }} + /> + )}
) } @@ -414,27 +416,28 @@ function Users() { /> ) : ( - (!isCurrentUser(record.id) && -
- } - colorType="red" - radiusType="circle" - border="gray-light" - backgroundColorType={'white'} - fontSize="12px" - fontFamily="InterMedium" - isLoading={record.username === userToRemove.username && userDeletedLoader} - onClick={() => { - deleteUser(record.username, record.user_type); - }} - />) + !isCurrentUser(record.id) && ( +
+ } + colorType="red" + radiusType="circle" + border="gray-light" + backgroundColorType={'white'} + fontSize="12px" + fontFamily="InterMedium" + isLoading={record.username === userToRemove.username && userDeletedLoader} + onClick={() => { + deleteUser(record.username, record.user_type); + }} + /> + ) )}
) @@ -520,41 +523,27 @@ function Users() { /> )}
- -
- -
-

Add a new user

- - - } - width="450px" - rBtnText="Create" - lBtnText="Cancel" - lBtnClick={() => { - addUserModalFlip(false); - setCreateUserLoader(false); - }} - clickOutside={() => { + { setCreateUserLoader(false); addUserModalFlip(false); }} - rBtnClick={() => { - setCreateUserLoader(true); - createUserRef.current(); - }} - isLoading={createUserLoader} + destroyOnClose={true} + width="650px" open={addUserModalIsOpen} > handleAddUser(userData)} handleLoader={(e) => setCreateUserLoader(e)} + closeModal={(userData) => { + handleAddUser(userData); + }} + isLoading={createUserLoader} /> -
+