diff --git a/ui_src/src/components/selectCheckBox/index.js b/ui_src/src/components/selectCheckBox/index.js index 210e0de2e..8b75007c3 100644 --- a/ui_src/src/components/selectCheckBox/index.js +++ b/ui_src/src/components/selectCheckBox/index.js @@ -15,7 +15,7 @@ import './style.scss'; import CheckCircleIcon from '@material-ui/icons/CheckCircle'; import React from 'react'; -const SelectCheckBox = ({ selectOptions, selectedOption, allowEdit = true, handleOnClick, button, hideCircle, vertical }) => { +const SelectCheckBox = ({ selectOptions, selectedOption, allowEdit = true, handleOnClick, button, hideCircle, vertical, disabled }) => { return (
{selectOptions.map((value) => { @@ -25,11 +25,11 @@ const SelectCheckBox = ({ selectOptions, selectedOption, allowEdit = true, handl className={ selectedOption === value.value ? 'option-wrapper selected' - : !value.disabled && allowEdit + : !value.disabled && allowEdit && !disabled ? 'option-wrapper allowed' : 'option-wrapper not-allowed' } - onClick={() => handleOnClick(value)} + onClick={() => !disabled && handleOnClick(value)} style={{ width: vertical && '100%' }} >
diff --git a/ui_src/src/components/table/index.js b/ui_src/src/components/table/index.js index 976d2ba3f..7acc0b50d 100644 --- a/ui_src/src/components/table/index.js +++ b/ui_src/src/components/table/index.js @@ -15,9 +15,10 @@ import './style.scss'; import { Table as CustomTable } from 'antd'; import React, { useEffect, useState } from 'react'; -const Table = ({ columns, data, title, tableRowClassname, className }) => { +const Table = ({ columns, data, title, tableRowClassname, className, onSelectRow }) => { const [windowHeight, setWindowHeight] = useState(window.innerHeight); const [numRows, setNumRows] = useState(10); + useEffect(() => { const handleResize = () => { setWindowHeight(window.innerHeight); @@ -60,6 +61,13 @@ const Table = ({ columns, data, title, tableRowClassname, className }) => { {...fieldProps} pagination={{ pageSize: numRows, itemRender: itemRender, hideOnSinglePage: true, responsive: false }} rowKey={(record) => record.id} + onRow={(record, rowIndex) => { + return { + onClick: (event) => { + onSelectRow(record, rowIndex); + } + }; + }} /> ); }; diff --git a/ui_src/src/connectors/assets/memphisIcon.svg b/ui_src/src/connectors/assets/memphisIcon.svg new file mode 100644 index 000000000..ae75393c0 --- /dev/null +++ b/ui_src/src/connectors/assets/memphisIcon.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui_src/src/connectors/index.js b/ui_src/src/connectors/index.js index 19b75ebce..cf3209b49 100644 --- a/ui_src/src/connectors/index.js +++ b/ui_src/src/connectors/index.js @@ -1,9 +1,11 @@ import S3LogoIcon from './assets/s3LogoIcon.svg'; import KafkaIcon from './assets/kafkaIcon.svg'; import RedisIcon from './assets/redisIcon.svg'; +import MemphisIcon from './assets/memphisIcon.svg'; import { kafka } from './kafka'; import { redis } from './redis'; +import { memphis } from './memphis'; export const connectorTypesSource = [ { name: 'Kafka', icon: KafkaIcon, comment: 'Supported version: 0.9 and above', inputs: kafka }, @@ -13,5 +15,6 @@ export const connectorTypesSource = [ export const connectorTypesSink = [ { name: 'Kafka', icon: KafkaIcon, comment: 'Supported version: 0.9 and above', inputs: kafka }, { name: 'Redis', icon: RedisIcon, inputs: redis }, + { name: 'Memphis',icon: MemphisIcon, inputs: memphis }, { name: 'S3', icon: S3LogoIcon, disabled: true, soon: true } ]; diff --git a/ui_src/src/connectors/memphis.js b/ui_src/src/connectors/memphis.js new file mode 100644 index 000000000..6a8de5972 --- /dev/null +++ b/ui_src/src/connectors/memphis.js @@ -0,0 +1,96 @@ +export const memphis = { + Sink: [ + { + name: 'name', + display: 'Connector name', + type: 'string', + required: true, + description: 'Note that the sink connector name is also consumer group name' + }, + { + name: 'route_strategy', + display: 'Route Strategy', + type: 'select', + options: ['Station Name', 'Header'], + required: true, + children: true, + 'Station Name': [ + { + name: 'station_name', + display: 'Station Name', + type: 'string', + required: true, + description: 'The name of the station to route messages to (all messages will be routed to the station)' + } + ], + Header: [ + { + name: 'header_name', + display: 'Header Name', + type: 'string', + required: true, + description: 'The name of the header in Memphis message, to take the station name from. for multiple stations use comma separated values ex. station1,station2' + } + ] + }, + { + name: 'dest_station_config', + display: 'Destination Station Config', + type: 'select', + options: ['Default Station', 'Custom Station'], + required: true, + description: 'if a station dose not exist, it will be created with the chosen configuration - Default / Custom', + children: true, + 'Default Station': [], + 'Custom Station': [ + { + name: 'partition_number', + display: 'Partition Number', + type: 'string', + required: true, + description: 'The number of partitions in the station' + }, + { + name : 'retention_policy', + display : 'Retention Policy', + type : 'select', + options : ['Time', 'Size', 'Messages', 'Ack'], + required : true, + description : 'choose retention policy', + children : true, + Time : [ + { + name: 'retention_value', + display: 'Value', + type: 'string', + required: true, + description: 'retention time value in seconds', + placeholder: 0 + } + ], + Size : [ + { + name: 'retention_value', + display: 'Value', + type: 'string', + required: true, + description: 'retentnion size in bytes', + placeholder: 0 + } + ], + Messages : [ + { + name: 'retention_value', + display: 'Value', + type: 'string', + required: true, + description: 'retention messages number', + placeholder: 0 + } + ], + Ack: [] + } + ] + } + ] +}; diff --git a/ui_src/src/connectors/redis.js b/ui_src/src/connectors/redis.js index 8519c4826..5cc89f794 100644 --- a/ui_src/src/connectors/redis.js +++ b/ui_src/src/connectors/redis.js @@ -25,16 +25,36 @@ export const redis = { children: false }, { - name: 'username', - display: 'Username', - type: 'string', - required: false - }, - { - name: 'password', - display: 'Password', - type: 'string', - required: false + name: 'authentication', + display: 'Authentication', + type: 'select', + options: ['No authentication', 'Username and Password', 'Password Only'], + required: true, + description: 'No Authentication, Username and Password, Password Only', + children: true, + 'Username and Password': [ + { + name: 'username', + display: 'Username', + type: 'string', + required: true + }, + { + name: 'password', + display: 'Password', + type: 'string', + required: true + } + ], + 'Password Only': [ + { + name: 'password', + display: 'Password', + type: 'string', + required: true + } + ], + 'No authentication': [] }, { name: 'db', diff --git a/ui_src/src/domain/users/createUserDetails/index.js b/ui_src/src/domain/users/createUserDetails/index.js index d18989e86..8af93f11a 100644 --- a/ui_src/src/domain/users/createUserDetails/index.js +++ b/ui_src/src/domain/users/createUserDetails/index.js @@ -15,13 +15,16 @@ import './style.scss'; import React, { useContext, useEffect, useState } from 'react'; import { Form } from 'antd'; import { HiLockClosed } from 'react-icons/hi'; +import { ArrowDropDownRounded } from '@material-ui/icons'; import Input from '../../../components/Input'; import Button from '../../../components/button'; import SelectComponent from '../../../components/select'; +import { Select } from 'antd'; import { httpRequest } from '../../../services/http'; import { useGetAllowedActions } from '../../../services/genericServices'; import { ApiEndpoints } from '../../../const/apiEndpoints'; import SelectCheckBox from '../../../components/selectCheckBox'; +import RadioButton from '../../../components/radioButton'; import { generator } from '../../../services/generator'; import { ReactComponent as RefreshIcon } from '../../../assets/images/refresh.svg'; @@ -30,14 +33,14 @@ import { isCloud, showUpgradePlan } from '../../../services/valueConvertor'; import { Context } from '../../../hooks/store'; import UpgradePlans from '../../../components/upgradePlans'; -const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, isLoading, clientType = false }) => { +const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, isLoading, clientType = false, selectedRow }) => { const [state, dispatch] = useContext(Context); const [creationForm] = Form.useForm(); const [formFields, setFormFields] = useState({ username: '', password: '' }); - const [userType, setUserType] = useState(clientType ? 'application' : 'management'); + const [userType, setUserType] = useState(selectedRow?.user_type === 'application' ? 'application' : clientType ? 'application' : 'management'); const [userViolation, setUserViolation] = useState(false); const userTypeOptions = [ { @@ -55,12 +58,34 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, disabled: false } ]; + const rbacTypeOptions = [ + { + id: 1, + value: 'pattern', + label: 'Pattern' + }, + { + id: 2, + value: 'stations', + label: 'Stations' + } + ]; + const [stationsList, setStationsList] = useState([]); + const [rbacTypeWrite, setRbacTypeWrite] = useState('pattern'); + const [rbacTypeRead, setRbacTypeRead] = useState('pattern'); + const [isDisabled, setIsDisabled] = useState(false); const getAllowedActions = useGetAllowedActions(); + useEffect(() => { + getAllStations(); createUserRef.current = onFinish; }, []); + useEffect(() => { + selectedRow && setIsDisabled(true); + }, [selectedRow]); + const updateFormState = (field, value) => { let updatedValue = { ...formFields }; updatedValue[field] = value; @@ -78,6 +103,7 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, const onFinish = async () => { try { let canCreate = isCloud() ? false : true; + console.log('formFields', creationForm.getFieldsValue()); const fieldsValue = await creationForm.validateFields(); if (fieldsValue?.errorFields) { handleLoader(false); @@ -91,6 +117,24 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, try { handleLoader(true); const bodyRequest = fieldsValue; + if (userType === 'application') { + bodyRequest['allow_read_permissions'] = + formFields?.allow_read_permissions?.length === 0 || + formFields?.allow_read_permissions === null || + formFields?.allow_read_permissions === undefined + ? null + : rbacTypeRead === 'stations' + ? formFields?.allow_read_permissions + : [formFields?.allow_read_permissions]; + bodyRequest['allow_write_permissions'] = + formFields?.allow_write_permissions?.length === 0 || + formFields?.allow_write_permissions === null || + formFields?.allow_write_permissions === undefined + ? null + : rbacTypeWrite === 'stations' + ? formFields?.allow_write_permissions + : [formFields?.allow_write_permissions]; + } const data = await httpRequest('POST', ApiEndpoints.ADD_USER, bodyRequest); if (data) { closeModal(data); @@ -107,6 +151,19 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, } }; + const getAllStations = async () => { + try { + const res = await httpRequest('GET', `${ApiEndpoints.GET_STATIONS}`); + setStationsList( + res?.stations?.map((station) => { + return { label: station?.station?.name, value: station?.station?.name }; + }) + ); + } catch (err) { + return; + } + }; + const generateNewPassword = () => { const newPassword = generator(); updateFormState('password', newPassword); @@ -121,7 +178,7 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList, return (
-
+
type.value === 'application') : userTypeOptions} handleOnClick={(e) => handleUserTypeChanged(e.value)} selectedOption={userType} + disabled={isDisabled} />
-
+

User details

- -
-

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

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

Email*

+ updateFormState('username', e.target.value)} + onChange={(e) => { + updateFormState('username', e.target.value); + creationForm.setFieldsValue({ username: e.target.value }); + }} + value={formFields?.username || selectedRow?.username} + disabled={isDisabled} + /> +
+ +

Full name*

+ updateFormState('full_name', e.target.value)} + onChange={(e) => { + updateFormState('full_name', e.target.value); + creationForm.setFieldsValue({ full_name: e.target.value }); + }} + value={formFields?.full_name || selectedRow?.full_name} + disabled={isDisabled} + /> +
- - {userType === 'management' && ( - <> - {userType === 'management' && ( + )} + {userType === 'management' && isCloud() && ( +
+ +

Team

+ updateFormState('team', e.target.value)} + onChange={(e) => { + updateFormState('team', e.target.value); + creationForm.setFieldsValue({ team: e.target.value }); + }} + value={formFields?.team || selectedRow?.team} + disabled={isDisabled} + /> +
+ +

Position

+ updateFormState('position', e.target.value)} + onChange={(e) => { + updateFormState('position', e.target.value); + creationForm.setFieldsValue({ position: e.target.value }); + }} + value={formFields?.position || selectedRow?.position} + disabled={isDisabled} + /> +
+
+ )} + {userType === 'management' && !isCloud() && ( +
+ +

Username*

+ updateFormState('username', e.target.value)} + onChange={(e) => { + updateFormState('username', e.target.value); + creationForm.setFieldsValue({ username: e.target.value }); + }} + value={formFields?.username || selectedRow?.username} + disabled={isDisabled} + /> +
+ +
+

Set password or generate one*

+ + +

Generate

+
+
+ { + updateFormState('password', e.target.value); + creationForm.setFieldsValue({ ['password']: e.target.value }); + }} + onBlur={(e) => { + updateFormState('password', e.target.value); + }} + disabled={isDisabled} + /> +
+
+ )} + {userType === 'management' && !isCloud() && ( +
+ +

Full name

+ updateFormState('full_name', e.target.value)} + onChange={(e) => { + updateFormState('full_name', e.target.value); + creationForm.setFieldsValue({ full_name: e.target.value }); + }} + value={formFields?.full_name || selectedRow?.full_name} + disabled={isDisabled} + /> +
+ +

Team

+ updateFormState('team', e.target.value)} + onChange={(e) => { + updateFormState('team', e.target.value); + creationForm.setFieldsValue({ team: e.target.value }); + }} + value={formFields?.team || selectedRow?.team} + disabled={isDisabled} + /> +
+ +

Position

+ updateFormState('position', e.target.value)} + onChange={(e) => { + updateFormState('position', e.target.value); + creationForm.setFieldsValue({ position: e.target.value }); + }} + value={formFields?.position || selectedRow?.position} + disabled={isDisabled} + /> +
+
+ )} + {userType === 'application' && ( +
+ +

Username*

+ updateFormState('username', e.target.value)} + onChange={(e) => { + updateFormState('username', e.target.value); + creationForm.setFieldsValue({ username: e.target.value }); + }} + value={formFields?.username || selectedRow?.username} + disabled={isDisabled} + /> +
+ {localStorage.getItem(LOCAL_STORAGE_USER_PASS_BASED_AUTH) === 'true' && ( -
-

{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} - /> +
+

Set password or generate one*

+ + +

Generate

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

Description

updateFormState('description', e.target.value)} - onChange={(e) => updateFormState('description', e.target.value)} - value={formFields.description} + value={selectedRow ? '*******' : formFields?.password} + onChange={(e) => { + updateFormState('password', e.target.value); + creationForm.setFieldsValue({ password: e.target.value }); + }} + onBlur={(e) => { + updateFormState('password', e.target.value); + }} + disabled={isDisabled} /> -
-
- +
+ )} +
)} -
- - {((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('password', e.target.value); - // setGeneratedPassword(e.target.value); - // creationForm.setFieldsValue({ ['generatedPassword']: e.target.value }); - }} - onBlur={(e) => { - updateFormState('password', e.target.value); + updateFormState('description', e.target.value); + creationForm.setFieldsValue({ description: e.target.value }); }} + value={formFields?.description || selectedRow?.description} + disabled={isDisabled} /> -
- -
+
+
+ )} +
+ {userType === 'management' && ( + <> +
+ +

Roles

+ +
+ } + colorType="black" + backgroundColorType="none" + fontFamily="Inter" + borderColorType="gray" + radiusType="semi-round" + height="40px" + fontSize="12px" + popupClassName="select-options" + placeholder="Select role" + disabled + /> + +

Permissions

+ +
+ } + colorType="black" + backgroundColorType="none" + fontFamily="Inter" + borderColorType="gray" + radiusType="semi-round" + height="40px" + fontSize="12px" + popupClassName="select-options" + placeholder="Select permissions" + disabled + /> + +

Tenants

+ +
+ } + colorType="black" + backgroundColorType="none" + fontFamily="Inter" + borderColorType="gray" + radiusType="semi-round" + height="40px" + fontSize="12px" + popupClassName="select-options" + placeholder="Select tenants" + disabled + /> +
+ )} - <> -
- -

Roles

- -
- - -

Permissions

- + + {userType === 'application' && ( +
+ +

Can read from (R)

+ setRbacTypeRead(e.target.value)} + disabled={isDisabled} + />
- - -

Tenants

- +
+ + {rbacTypeRead === 'stations' ? ( + } + showArrow + mode="tags" + placeholder={'*'} + value={selectedRow?.allow_read_permissions || []} + onChange={(e) => { + updateFormState('allow_read_permissions', e); + creationForm.setFieldsValue({ allow_read_permissions: e }); + }} + style={{ width: '100%' }} + popupClassName="select-options" + disabled={isDisabled} + > + )} + +
+
+ )} + {userType === 'application' && ( +
+ +

Can write to (W)

+ setRbacTypeWrite(e.target.value)} + disabled={isDisabled} + />
- +
+ + {rbacTypeWrite === 'stations' ? ( + } + showArrow + mode="tags" + placeholder={'*'} + value={selectedRow?.allow_write_permissions || []} + onChange={(e) => { + updateFormState('allow_write_permissions', e); + creationForm.setFieldsValue({ allow_write_permissions: e }); + }} + style={{ width: '100%', backgroundColor: 'none' }} + popupClassName="select-options" + disabled={isDisabled} + > + )} + +
- + )} + {userViolation && (
@@ -392,7 +777,7 @@ const CreateUserDetails = ({ createUserRef, closeModal, handleLoader, userList,
{ setCreateUserLoader(false); addUserModalFlip(false); + setSelectedRow(null); }} destroyOnClose={true} width="650px" - open={addUserModalIsOpen} + open={addUserModalIsOpen || selectedRow} > setCreateUserLoader(e)} closeModal={(userData) => { - handleAddUser(userData); + selectedRow ? setSelectedRow(null) : handleAddUser(userData); }} isLoading={createUserLoader} /> diff --git a/ui_src/src/domain/users/style.scss b/ui_src/src/domain/users/style.scss index 51152f4b8..31857516c 100644 --- a/ui_src/src/domain/users/style.scss +++ b/ui_src/src/domain/users/style.scss @@ -1,6 +1,6 @@ .users-container { width: calc(100% - var(--main-container-sidebar-width)); - transition: .3s ease-in-out; + transition: 0.3s ease-in-out; position: absolute; padding: 1vw; min-width: 700px;