From 5abf3eca5797af04e7eda5507370732aaedf9041 Mon Sep 17 00:00:00 2001 From: Radhika Vissamsetty Date: Thu, 31 Aug 2023 16:56:45 +0530 Subject: [PATCH] Add new tab for ServiceAccounts in NS admin UI --- .../components/NamespaceAdmin/AdminTabs.tsx | 7 + .../NamespaceAdmin/ServiceAccounts/index.tsx | 272 ++++++++++++++++++ app/cdap/text/text-en.yaml | 18 ++ 3 files changed, 297 insertions(+) create mode 100644 app/cdap/components/NamespaceAdmin/ServiceAccounts/index.tsx diff --git a/app/cdap/components/NamespaceAdmin/AdminTabs.tsx b/app/cdap/components/NamespaceAdmin/AdminTabs.tsx index 07f641df887..db8c0ea9ff6 100644 --- a/app/cdap/components/NamespaceAdmin/AdminTabs.tsx +++ b/app/cdap/components/NamespaceAdmin/AdminTabs.tsx @@ -29,6 +29,7 @@ import Tab from '@material-ui/core/Tab'; import TabContext from '@material-ui/lab/TabContext'; import styled from 'styled-components'; import { useLocation } from 'react-router'; +import ServiceAccounts from './ServiceAccounts'; const StyledTabs = styled(Tabs)` border-bottom: 1px solid #e8e8e8; @@ -91,6 +92,11 @@ export const AdminTabs = () => { value={`${baseNSPath}/connections`} /> + {sourceControlManagementEnabled && ( { + diff --git a/app/cdap/components/NamespaceAdmin/ServiceAccounts/index.tsx b/app/cdap/components/NamespaceAdmin/ServiceAccounts/index.tsx new file mode 100644 index 00000000000..f74586f503c --- /dev/null +++ b/app/cdap/components/NamespaceAdmin/ServiceAccounts/index.tsx @@ -0,0 +1,272 @@ +/* + * Copyright © 2023 Cask Data, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +import React, { useState } from 'react'; +import { connect } from 'react-redux'; + +import Table from 'components/shared/Table'; +import TableHeader from 'components/shared/Table/TableHeader'; +import TableRow from 'components/shared/Table/TableRow'; +import TableCell from 'components/shared/Table/TableCell'; +import TableBody from 'components/shared/Table/TableBody'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import ActionsPopover, { IAction } from 'components/shared/ActionsPopover'; +import T from 'i18n-react'; +import InputAdornment from '@material-ui/core/InputAdornment'; +import HelpIcon from '@material-ui/icons/Help'; +import Box from '@material-ui/core/Box'; +import styled from 'styled-components'; +import IconButton from '@material-ui/core/IconButton'; +import { ConfirmDialog } from 'components/shared/ConfirmDialog'; +import { boolean } from '@storybook/addon-knobs'; + +const PREFIX = 'features.ServiceAccounts'; + +const SubTitleBox = styled(Box)` + margin-bottom: 15px; +`; + +const StyledTextField = styled(TextField)` + & .MuiInputLabel-shrink { + font-size: 15px; + } + & .MuiOutlinedInput-notchedOutline { + font-size: 14px; + border-color: #80868b; + } + & .MuiFormHelperText-root { + margin-left: 0px; + font-size: 12px; + } +`; + +const ServiceAccountsView = ({ serviceacnts }) => { + // mock data + // serviceacnts = [{ name: 'serviceaccount1' }, { name: 'serviceaccount2' }]; + + const [showPopover, setShowPopover] = useState(false); + const [isDeleteModalOpen, setDeleteModalOpen] = useState(false); + const [selectedServiceAcnt, setSelectedServiceAcnt] = useState(null); + const [open, setOpen] = React.useState(false); + const [deleteErrorMsg, setDeleteErrorMsg] = useState(null); + const [extendedErrorMsg, setExtendedErrorMsg] = useState(null); + const [saveStatusMsg, setSaveStatusMsg] = useState(null); + const [saveStatusDetails, setSaveStatusDetails] = useState(null); + const [saveStatus, setSaveStatus] = useState(null); // 'success' | 'info' | 'warning' | 'error'; + const [isValidated, setIsValidated] = useState(false); + + const handleClose = () => { + setOpen(false); + setSaveStatusMsg(null); + setSaveStatusDetails(null); + }; + + const handleSave = () => { + // alert('saving ' + selectedServiceAcnt); + // setSaveStatus('error'); + // setSaveStatusMsg(T.translate(`${PREFIX}.saveErrorMessage`)); + // setSaveStatusDetails({ + // response: + // 'Sample Error message for testing : Error occurred as save is not intergrated with API', + // }); + }; + + const addHandler = () => { + setOpen(true); + setSelectedServiceAcnt(null); + }; + + const handleValidate = () => { + const isValidateSuccess = false; + // To do: Call validate API + if (isValidateSuccess) { + setIsValidated(true); + setSaveStatus('success'); + setSaveStatusMsg(T.translate(`${PREFIX}.validationSuccessMessage`)); + } else { + setIsValidated(false); + setSaveStatus('error'); + setSaveStatusMsg(T.translate(`${PREFIX}.validationErrorMessage`)); + setSaveStatusDetails({ + response: 'Sample Error message for testing : Validation failed', + }); + } + }; + + const deleteHanlder = (serviceAcnt) => { + // alert('delete'); + // const sampleError = + // 'Error occurred : ' + + // "Exception in thread 'main' java.util.InputMismatchException" + + // 'at java.base/java.util.Scanner.throwFor(Scanner.java:939)' + + // 'at java.base/java.util.Scanner.next(Scanner.java:1594)' + + // 'at java.base/java.util.Scanner.nextFloat(Scanner.java:2496)' + + // 'at java.base/java.util.Scanner.throwFor(Scanner.java:939)' + + // 'at java.base/java.util.Scanner.next(Scanner.java:1594)' + + // 'at java.base/java.util.Scanner.nextFloat(Scanner.java:2496)' + + // 'at java.base/java.util.Scanner.throwFor(Scanner.java:939)' + + // 'at java.base/java.util.Scanner.next(Scanner.java:1594)' + + // 'at java.base/java.util.Scanner.nextFloat(Scanner.java:2496)'; + // setExtendedErrorMsg(sampleError); + // setDeleteErrorMsg(T.translate(`${PREFIX}.deleteErrorMessage`)); + }; + + const handleShowHelp = () => { + // to do + }; + + function showEditDialog(serviceAcnt) { + setSelectedServiceAcnt(serviceAcnt.name); + setOpen(true); + setShowPopover(!showPopover); + } + + const closeDeleteConfirmation = () => { + setDeleteModalOpen(false); + setDeleteErrorMsg(null); + }; + + const showDeleteConfirmation = (serviceAcnt) => { + setSelectedServiceAcnt(serviceAcnt.name); + setShowPopover(!showPopover); + setDeleteModalOpen(true); + }; + + const renderDeleteConfirm = () => { + return ( + + ); + }; + + const getEditDialogContent = () => { + return ( + { + setSelectedServiceAcnt(ev.target.value); + }} + InputProps={{ + endAdornment: ( + + + + + + ), + }} + /> + ); + }; + + const renderEditDialog = () => { + return ( + + ); + }; + + return ( +
+ {!serviceacnts && ( + + + + )} + + + + {T.translate(`${PREFIX}.serviceAccount`)} + + + + + + {serviceacnts && + serviceacnts.map((serviceAcnt) => { + const actions: IAction[] = [ + { + label: T.translate('commons.edit'), + actionFn: () => showEditDialog(serviceAcnt), + }, + { + label: 'separator', + }, + { + label: T.translate('commons.delete'), + actionFn: () => showDeleteConfirmation(serviceAcnt), + }, + ]; + + return ( + + {serviceAcnt.name} + + + + + ); + })} + +
+ {renderDeleteConfirm()} + {renderEditDialog()} +
+ ); +}; + +const mapStateToProps = (state) => { + return { + serviceacnts: state.serviceacnts, + }; +}; + +const ServiceAccounts = connect(mapStateToProps)(ServiceAccountsView); +export default ServiceAccounts; diff --git a/app/cdap/text/text-en.yaml b/app/cdap/text/text-en.yaml index 44a169e0f45..8b1834b7db2 100644 --- a/app/cdap/text/text-en.yaml +++ b/app/cdap/text/text-en.yaml @@ -5,6 +5,7 @@ commons: apply: Apply as: as back: Back + cancel: Cancel learnMore: Learn More gotIt: Ok, Got it. cookieBanner: This application uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic. @@ -96,6 +97,7 @@ commons: pipelines: Pipelines requiredFieldMissingMsg: Required field cannot be empty resource-center: Add entity + save: Save schemaLabel: Schema scope: Scope secondsShortLabel: secs @@ -107,6 +109,7 @@ commons: then: Then tracker: Cask Tracker typeLabel: Type + validate: Validate when: When wrangler: Cask Wrangler yesLabel: Yes @@ -3168,6 +3171,21 @@ features: symbolName: Symbol name ServiceEnableUtility: serviceNotFound: Cannot find {artifactName} artifact + ServiceAccounts: + addServiceAccount: Add service account + delete: Delete + deleteButtonText: Delete + cancelButtonText: Cancel + deleteConfirmation: Clicking Delete will remove *_{serviceaccount}_* from the default namespace. Are you sure you want to remove the service account? + deleteTitle: Delete service account + edit: Edit + editInputLabel: Pipeline Design Service Account + inputHelperText: Provide details of the service account for authorization + serviceAccount : Service account + deleteErrorMessage: Unable to delete service account + saveErrorMessage: Failed to save the service account changes + validationSuccessMessage: Service account validation successful + validationErrorMessage: Service account validation failed SourceControlManagement: configModal: auth: