k2hr3-app
Version:
K2HR3 Web Application is K2hdkc based Resource and Roles and policy Rules
1,456 lines (1,318 loc) • 41.5 kB
JSX
/*
*
* K2HR3 Web Application
*
* Copyright 2017 Yahoo Japan Corporation.
*
* K2HR3 is K2hdkc based Resource and Roles and policy Rules, gathers
* common management information for the cloud.
* K2HR3 can dynamically manage information as "who", "what", "operate".
* These are stored as roles, resources, policies in K2hdkc, and the
* client system can dynamically read and modify these information.
*
* For the full copyright and license information, please view
* the license file that was distributed with this source code.
*
* AUTHOR: Takeshi Nakatani
* CREATE: Tue Aug 15 2017
* REVISION:
*
*/
import React from 'react';
import ReactDOM from 'react-dom'; // eslint-disable-line no-unused-vars
import PropTypes from 'prop-types';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Drawer from '@mui/material/Drawer';
import Chip from '@mui/material/Chip';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import ListItemIcon from '@mui/material/ListItemIcon';
import Collapse from '@mui/material/Collapse';
import Box from '@mui/material/Box';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import MenuIcon from '@mui/icons-material/Menu';
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
import LabelIcon from '@mui/icons-material/Label';
import OwnerIcon from '@mui/icons-material/Person';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import EditIcon from '@mui/icons-material/Edit';
import { r3MainTree } from './r3styles';
import R3LocalTenantDialog from './r3localtenantdialog';
import R3PopupMsgDialog from './r3popupmsgdialog';
import R3Message from '../util/r3message';
import { serviceType, errorType } from '../util/r3types';
import { localTenantPrefix } from '../util/r3define';
import { r3DeepCompare, r3IsEmptyEntity, r3IsEmptyStringObject, r3IsEmptyString, r3CompareCaseString, r3IsEmptyEntityObject, r3IsSafeTypedEntity, r3DeepClone } from '../util/r3util';
// For context
import { R3CommonContext } from './r3commoncontext';
//
// Local variables
//
const menuValues = {
dock: 'DOCK',
about: 'ABOUT',
license: 'LICENSES_TOP',
noLicense: 'NOLICENSE',
sign: 'SIGNINOUT',
userName: 'USERNAME',
localTenant: 'LOCALTENANT'
};
const componentKeyIds = {
topListId: 'mainTree-top-list',
dummyBarMenuId: 'mainTree-dummybar-main-menu',
dummyLicenseMenuId: 'mainTree-dummybar-license-menu',
tenantMenuId: 'maintree-select-tenant-menu',
topList: 'mainTree-top-list',
subHeaderList: 'mainTree-subheader-list',
dummyBarList: 'mainTree-dummybar-list',
noTenantList: 'mainTree-no-tenant-list'
};
const tooltipValues = {
mainMenu: 'mainMenu',
tenantEdit: 'tenantEdit',
tenantMenu: 'tenantMenu'
};
//
// Main TreeView Class
//
export default class R3MainTree extends React.Component
{
// Set context as this.context
static contextType = R3CommonContext;
static propTypes = {
r3provider: PropTypes.object.isRequired,
title: PropTypes.string.isRequired,
open: PropTypes.bool,
enDock: PropTypes.bool,
isDocking: PropTypes.bool,
licensesObj: PropTypes.object,
editableLocalTenant: PropTypes.bool,
userName: PropTypes.string,
tenants: PropTypes.array.isRequired,
treeList: PropTypes.array,
selectedTenant: PropTypes.object,
selectedType: PropTypes.string,
selectedService: PropTypes.string,
selectedPath: PropTypes.string,
onTenantChange: PropTypes.func.isRequired,
onLocalTenantCreate: PropTypes.func.isRequired,
onLocalTenantChange: PropTypes.func.isRequired,
onLocalTenantDelete: PropTypes.func.isRequired,
onTypeItemChange: PropTypes.func.isRequired,
onListItemChange: PropTypes.func.isRequired,
onNameItemInServiceChange: PropTypes.func.isRequired,
onTypeInServiceChange: PropTypes.func.isRequired,
onListItemInServiceChange: PropTypes.func.isRequired,
onPopupClose: PropTypes.func.isRequired,
onTreeDocking: PropTypes.func.isRequired,
onCheckUpdating: PropTypes.func,
onAbout: PropTypes.func.isRequired
};
static defaultProps = {
open: false,
enDock: true,
isDocking: true,
licensesObj: null,
editableLocalTenant: true,
userName: null,
treeList: null,
selectedTenant: null,
selectedType: null,
selectedService: null,
selectedPath: null,
onCheckUpdating: null
};
state = {
r3Message: null,
dummyBarMenuAnchorEl: null,
dummyLicenseMenuAnchorEl: null,
tenantMenuAnchorEl: null,
localTenantDialogOpen: false,
localTenantCreateMode: false,
tooltips: {
mainMenuTooltip: false,
tenantEditTooltip: false,
tenantMenuTooltip: false
},
collapseExpands: {}
};
constructor(props)
{
super(props);
// Binding(do not define handlers as arrow functions for performance)
this.handleTenantEditButton = this.handleTenantEditButton.bind(this);
this.handLocalTenantDialogClose = this.handLocalTenantDialogClose.bind(this);
this.handleTenantMenuButton = this.handleTenantMenuButton.bind(this);
this.handleTenantMenuClose = this.handleTenantMenuClose.bind(this);
this.handleTypeItemChange = this.handleTypeItemChange.bind(this);
this.handleListItemChange = this.handleListItemChange.bind(this);
this.handleNameItemInServiceChange = this.handleNameItemInServiceChange.bind(this);
this.handleTypeInServiceChange = this.handleTypeInServiceChange.bind(this);
this.handleListItemInServiceChange = this.handleListItemInServiceChange.bind(this);
this.handleDummyBarButton = this.handleDummyBarButton.bind(this);
this.handleDummyBarMenuClose = this.handleDummyBarMenuClose.bind(this);
this.handleTreePopupClose = this.handleTreePopupClose.bind(this);
this.handMessageDialogClose = this.handMessageDialogClose.bind(this);
// styles
this.sxClasses = r3MainTree(props.theme);
}
handleTenantEditButton(event) // eslint-disable-line no-unused-vars
{
if(!this.checkContentUpdating()){
return;
}
this.setState({
localTenantDialogOpen: true,
localTenantCreateMode: false,
tooltips: {
tenantEditTooltip: false
}
});
}
handLocalTenantDialogClose(event, reason, result, newTenantName, tenantId, newTenantDisplay, newTenantDescription, newTenantUsers)
{
if(!result){
this.setState({
localTenantDialogOpen: false,
localTenantCreateMode: false,
r3Message: null,
tooltips: {
tenantEditTooltip: false
}
});
return;
}
// [NOTE]
// Value checking is completed in R3LocalTenantDialog.
// No checks are required here.
//
//
// Call parent handler for update/create tenant
//
if(this.state.localTenantCreateMode){
// create
this.props.onLocalTenantCreate(newTenantName, newTenantDisplay, newTenantDescription, newTenantUsers);
}else if(!r3IsSafeTypedEntity(newTenantUsers, 'array') || 0 === newTenantUsers.length){
// delete
this.props.onLocalTenantDelete(newTenantName, tenantId);
}else{
// update
this.props.onLocalTenantChange(newTenantName, tenantId, newTenantDisplay, newTenantDescription, newTenantUsers);
}
// close tenant dialog
this.setState({
localTenantDialogOpen: false,
localTenantCreateMode: false,
r3Message: null,
tooltips: {
tenantEditTooltip: false
}
});
}
handleTenantMenuButton(event)
{
this.setState({
tenantMenuAnchorEl: event.currentTarget,
tooltips: {
tenantMenuTooltip: false
}
});
}
handleTenantMenuClose(event) // eslint-disable-line no-unused-vars
{
this.setState({
tenantMenuAnchorEl: null
});
}
handleTenantChange = (event, value) =>
{
if(this.checkContentUpdating()){
this.props.tenants.map( (item, pos) => {
if(value === pos){
// if selected tenant is changed, we need to clear collapseExpands{}.
//
if( !r3IsEmptyEntityObject(this.props.selectedTenant, 'name') &&
!r3IsEmptyEntityObject(this.props.selectedTenant, 'display') &&
!r3IsEmptyEntityObject(this.props.selectedTenant, 'id') &&
!r3IsEmptyEntityObject(this.props.selectedTenant, 'description')&&
!r3IsEmptyEntityObject(item, 'name') &&
!r3IsEmptyEntityObject(item, 'display') &&
item.name !== this.props.selectedTenant.name &&
item.display !== this.props.selectedTenant.display )
{
this.setState({
collapseExpands: {}
});
}
this.props.onTenantChange(item);
}
});
}
// closing menu
this.setState({
tenantMenuAnchorEl: null
});
};
handleCreateLocalTenant = (event) => // eslint-disable-line no-unused-vars
{
if(this.checkContentUpdating()){
// open dialog and closing menu
this.setState({
localTenantDialogOpen: true,
localTenantCreateMode: true,
tenantMenuAnchorEl: null
});
}
// closing menu
this.setState({
tenantMenuAnchorEl: null
});
};
updateCollapseChange(prevSelected, collapseKey)
{
let newCollapseExpands = Object.assign({}, this.state.collapseExpands);
if(r3IsEmptyEntityObject(newCollapseExpands, collapseKey) || !r3IsSafeTypedEntity(newCollapseExpands[collapseKey], 'boolean')){
// not found collapseKey's state, it means that item does not expand now.
if(prevSelected){
// now selected -> expanding
newCollapseExpands[collapseKey] = true; // something wrong, but continue...
}else{
// now not selected -> expanding
newCollapseExpands[collapseKey] = true;
}
}else{
if(prevSelected){
// now selected -> toggle
newCollapseExpands[collapseKey] = !newCollapseExpands[collapseKey];
}else{
// now not selected -> expanding
newCollapseExpands[collapseKey] = true;
}
}
this.setState({
collapseExpands: newCollapseExpands
});
}
handleTypeItemChange(event, type, prevSelected, collapseKey)
{
if(!this.checkContentUpdating()){
return;
}
this.updateCollapseChange(prevSelected, collapseKey);
this.props.onTypeItemChange(type);
}
handleListItemChange(event, type, path, prevSelected, collapseKey)
{
if(!this.checkContentUpdating()){
return;
}
this.updateCollapseChange(prevSelected, collapseKey);
this.props.onListItemChange(type, path);
}
handleNameItemInServiceChange(event, servicename, prevSelected, collapseKey)
{
if(!this.checkContentUpdating()){
return;
}
this.updateCollapseChange(prevSelected, collapseKey);
this.props.onNameItemInServiceChange(servicename);
}
handleTypeInServiceChange(event, servicename, type_in_service, prevSelected, collapseKey)
{
if(!this.checkContentUpdating()){
return;
}
this.updateCollapseChange(prevSelected, collapseKey);
this.props.onTypeInServiceChange(servicename, type_in_service);
}
handleListItemInServiceChange(event, servicename, type_in_service, path, prevSelected, collapseKey)
{
if(!this.checkContentUpdating()){
return;
}
this.updateCollapseChange(prevSelected, collapseKey);
this.props.onListItemInServiceChange(servicename, type_in_service, path);
}
handleTreePopupClose(event) // eslint-disable-line no-unused-vars
{
console.info('CALL - Drawer close');
this.props.onPopupClose();
}
handleDummyBarButton(event)
{
this.setState({
dummyBarMenuAnchorEl: event.currentTarget,
dummyLicenseMenuAnchorEl: null,
tooltips: {
mainMenuTooltip: false
}
});
}
handleDummyBarMenuClose(event) // eslint-disable-line no-unused-vars
{
this.setState({
dummyBarMenuAnchorEl: null,
dummyLicenseMenuAnchorEl: null
});
}
handleDummyBarClick = (event, value) =>
{
// [NOTE]
// The sub MenuItem for licenses menu is received in the OnClick event,
// not the OnChange event.
// Then, the value is undefined in the onClick event, so we change the
// behavior of the function with this value.
//
if(r3IsEmptyEntity(value)){
// Licenses
if(!r3IsEmptyString(event.target.innerText)){
this.props.onAbout(event.target.innerText);
}
}else{
if(menuValues.dock === value){
this.props.onTreeDocking(true);
}else if(menuValues.about === value){
this.props.onAbout(null);
}else if(menuValues.noLicense === value || menuValues.license === value){
if(!this.state.dummyLicenseMenuAnchorEl){
this.setState({
dummyLicenseMenuAnchorEl: event.currentTarget
});
return;
}
}else if(!isNaN(value)){
let _appmenu= this.context.r3Context.getSafeAppMenu();
let _pos = parseInt(value);
if( 0 <= _pos &&
undefined !== _appmenu && null !== _appmenu && _appmenu instanceof Array && _pos < _appmenu.length &&
!r3IsEmptyStringObject(_appmenu[_pos], 'url'))
{
window.open(_appmenu[_pos].url);
}
}
}
// to close drawer
if(!this.props.isDocking){
this.props.onPopupClose();
}
// closing menu
this.setState({
dummyBarMenuAnchorEl: null,
dummyLicenseMenuAnchorEl: null
});
};
handMessageDialogClose(event, reason, result) // eslint-disable-line no-unused-vars
{
this.setState({
r3Message: null
});
}
handTooltipChange = (event, type, isOpen) =>
{
if(tooltipValues.mainMenu === type){
this.setState({
tooltips: {
mainMenuTooltip: isOpen
}
});
}else if(tooltipValues.tenantEdit === type){
this.setState({
tooltips: {
tenantEditTooltip: isOpen
}
});
}else if(tooltipValues.tenantMenu === type){
this.setState({
tooltips: {
tenantMenuTooltip: isOpen
}
});
}
};
checkContentUpdating()
{
const { r3provider } = this.props;
if(null !== this.props.onCheckUpdating && this.props.onCheckUpdating()){
this.setState({
r3Message: new R3Message(r3provider.getR3TextRes().eNowUpdating, errorType)
});
return false;
}
return true;
}
getAllTenantNames()
{
let tenantNames = [];
for(let cnt = 0; cnt < this.props.tenants.length; ++cnt){
if(!r3IsEmptyEntityObject(this.props.tenants[cnt], 'name') && !r3IsEmptyString(this.props.tenants[cnt].name)){
tenantNames.push(this.props.tenants[cnt].name);
}
}
tenantNames.sort();
return tenantNames;
}
getLicensesMenuItems()
{
const { r3provider } = this.props;
let _menuitems = [];
if(r3IsEmptyEntity(this.props.licensesObj)){
_menuitems.push(
<MenuItem
disabled={ true }
key={ menuValues.noLicense }
>
{ r3provider.getR3TextRes().tResNoLicenseMenu }
</MenuItem>
);
}else{
let packages = Object.keys(this.props.licensesObj);
for(let cnt = 0; cnt < packages.length; ++cnt){
_menuitems.push(
<MenuItem
key={ packages[cnt] }
onClick={ (event) => this.handleDummyBarClick(event, null) }
>
{ packages[cnt] }
</MenuItem>
);
}
}
return _menuitems;
}
getMenuItems()
{
const { theme, r3provider } = this.props;
let _menuitems = [];
if(this.props.enDock){
_menuitems.push(
<MenuItem
key={ menuValues.dock }
onClick={ event => this.handleDummyBarClick(event, menuValues.dock) }
>
{ r3provider.getR3TextRes().tResDockTreeMenu }
</MenuItem>
);
}
let _appmenu = this.context.r3Context.getSafeAppMenu();
if(undefined !== _appmenu && null !== _appmenu && _appmenu instanceof Array && 0 < _appmenu.length){
for(let cnt = 0; cnt < _appmenu.length; ++cnt){
if(r3IsEmptyStringObject(_appmenu[cnt], 'name')){
continue;
}
_menuitems.push(
<MenuItem
key={ cnt }
onClick={ event => this.handleDummyBarClick(event, cnt) }
>
{ _appmenu[cnt].name }
</MenuItem>
);
}
}
// Licenses
_menuitems.push(
<MenuItem
key={ menuValues.license }
onClick={ event => this.handleDummyBarClick(event, menuValues.license) }
>
{ r3provider.getR3TextRes().tResLicensesMenu }
<ListItemIcon>
<ArrowRightIcon
sx={ this.sxClasses.menuRightIcon }
/>
</ListItemIcon>
<Menu
id={ componentKeyIds.dummyLicenseMenuId }
anchorEl={ this.state.dummyLicenseMenuAnchorEl }
open={ Boolean(this.state.dummyLicenseMenuAnchorEl) }
onClose={ this.handleDummyBarMenuClose }
{ ...theme.r3MainTree.licenseMenu }
>
{ this.getLicensesMenuItems() }
</Menu>
</MenuItem>
);
// About
_menuitems.push(
<MenuItem
key={ menuValues.about }
onClick={ event => this.handleDummyBarClick(event, menuValues.about) }
>
{ r3provider.getR3TextRes().tResAboutMenu }
</MenuItem>
);
return _menuitems;
}
getTenantListMenuItems()
{
const { r3provider } = this.props;
if(!(this.props.tenants instanceof Array) || 0 === this.props.tenants.length){
return (
<MenuItem
key={ componentKeyIds.noTenantList }
disabled={ (r3IsEmptyString(this.props.userName) || !this.props.editableLocalTenant) }
>
{ r3provider.getR3TextRes().tResNoTenantLabel }
</MenuItem>
);
}else{
return (
this.props.tenants.map( (item, pos) => {
return (
<MenuItem
key={ pos }
onClick={ event => this.handleTenantChange(event, pos) }
>
{ (r3IsEmptyString(item.display) ? r3provider.getR3TextRes().tResUnknownTenantLabel : item.display) }
</MenuItem>
);
})
);
}
}
getLocalTenantMenuItem()
{
const { r3provider } = this.props;
if(r3IsEmptyString(this.props.userName) || !this.props.editableLocalTenant){
return;
}
return (
<MenuItem
key={ menuValues.localTenant }
onClick={ event => this.handleCreateLocalTenant(event) }
>
<Typography
sx={ this.sxClasses.localTenantMenu }
>
{ r3provider.getR3TextRes().tResAddLocalTenantMenu }
</Typography>
</MenuItem>
);
}
getLocalTenantMenuDivider()
{
if(r3IsEmptyString(this.props.userName) || !this.props.editableLocalTenant){
return;
}
return (
<Divider />
);
}
getDummyBarSubHeader()
{
const { theme, r3provider } = this.props;
let themeToolbar = this.props.enDock ? theme.r3MainTree.dummyBarToolbar : theme.r3MainTree.smallDummyBarToolbar;
return (
<AppBar
{ ...theme.r3MainTree.dummyBarAppbar }
sx={ this.sxClasses.dummyBarAppbar }
>
<Toolbar
{ ...themeToolbar }
sx={ this.sxClasses.dummyBarToolbar }
>
<Tooltip
title={ r3provider.getR3TextRes().tResMainMenuTT }
open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.mainMenuTooltip, 'boolean')) ? false : this.state.tooltips.mainMenuTooltip) }
>
<IconButton
onClick={ this.handleDummyBarButton }
onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.mainMenu, true) }
onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.mainMenu, false) }
aria-owns={ this.state.dummyBarMenuAnchorEl ? componentKeyIds.dummyBarMenuId : undefined }
{ ...theme.r3MainTree.dummyBarMainMenuButton }
size="large"
>
<MenuIcon />
</IconButton>
</Tooltip>
<Menu
id={ componentKeyIds.dummyBarMenuId }
anchorEl={ this.state.dummyBarMenuAnchorEl }
open={ Boolean(this.state.dummyBarMenuAnchorEl) }
onClose={ this.handleDummyBarMenuClose }
{ ...theme.r3MainTree.dummyBarMainMenu }
>
{ this.getMenuItems() }
</Menu>
<Typography
{ ...theme.r3MainTree.title }
ex={ this.sxClasses.title }
>
{ this.props.title }
</Typography>
</Toolbar>
</AppBar>
);
}
getTenantSubHeaders()
{
const { theme, r3provider } = this.props;
let menuItem = this.getTenantListMenuItems();
let menuLocalTenantItem = this.getLocalTenantMenuItem();
let menuLocalTenantDivider = this.getLocalTenantMenuDivider();
let titleName = ((this.props.tenants instanceof Array && 0 < this.props.tenants.length) ? r3provider.getR3TextRes().tResUnselectedTenantLabel : r3provider.getR3TextRes().tResNoTenantLabel);
let themeToolbar = (this.props.enDock ? theme.r3MainTree.subheaderToolbar : theme.r3MainTree.smallSubheaderToolbar);
this.props.tenants.map( (tenant, pos) => { // eslint-disable-line no-unused-vars
if(r3DeepCompare(tenant, this.props.selectedTenant)){
titleName = tenant.display;
}
});
//
// Local tenant edit button
//
let tenantEditBtn;
if( !r3IsEmptyString(this.props.userName) &&
this.props.editableLocalTenant &&
!r3IsEmptyEntityObject(this.props.selectedTenant, 'name') &&
0 === this.props.selectedTenant.name.indexOf(localTenantPrefix) &&
r3IsSafeTypedEntity(this.props.selectedTenant.users, 'array') )
{
tenantEditBtn = (
<Tooltip
title={ r3provider.getR3TextRes().tResTenantEditTT }
open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.tenantEditTooltip, 'boolean')) ? false : this.state.tooltips.tenantEditTooltip) }
>
<IconButton
onClick={ this.handleTenantEditButton }
onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.tenantEdit, true) }
onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.tenantEdit, false) }
{ ...theme.r3MainTree.tenantEditButton }
size="large"
>
<EditIcon />
</IconButton>
</Tooltip>
);
}
let textLabel = (
<Typography
{ ...theme.r3MainTree.chipText }
sx={ this.sxClasses.chipText }
>
{ r3provider.getR3TextRes().tResTenantLabel }
</Typography>
);
return (
<AppBar
{ ...theme.r3MainTree.subheaderAppbar }
sx={ this.sxClasses.subheaderAppbar }
>
<Toolbar
{ ...themeToolbar }
sx={ this.sxClasses.subheaderToolbar }
>
<Chip
label={ textLabel }
{ ...theme.r3MainTree.chip }
sx={ this.sxClasses.chip }
/>
<Typography
{ ...theme.r3MainTree.tenantListText }
sx={ this.sxClasses.tenantListText }
>
{ titleName }
</Typography>
{ tenantEditBtn }
<Tooltip
title={ r3provider.getR3TextRes().tResSelectTenantTT }
open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.tenantMenuTooltip, 'boolean')) ? false : this.state.tooltips.tenantMenuTooltip) }
>
<IconButton
disabled={ r3IsEmptyString(this.props.userName) }
aria-owns={ this.state.tenantMenuAnchorEl ? componentKeyIds.tenantMenuId : undefined }
onClick={ this.handleTenantMenuButton }
onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.tenantMenu, true) }
onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.tenantMenu, false) }
{ ...theme.r3MainTree.tenantListButton }
size="large"
>
<MoreVertIcon />
</IconButton>
</Tooltip>
<Menu
id={ componentKeyIds.tenantMenuId }
anchorEl={ this.state.tenantMenuAnchorEl }
open={ Boolean(this.state.tenantMenuAnchorEl) }
onClose={ this.handleTenantMenuClose }
{ ...theme.r3MainTree.tenantListMenu }
>
{ menuItem }
{ menuLocalTenantDivider }
{ menuLocalTenantItem }
</Menu>
</Toolbar>
</AppBar>
);
}
getSubHeaders()
{
let dummyBarSubHeader;
if(!this.props.isDocking){
dummyBarSubHeader = this.getDummyBarSubHeader();
}
return (
<Box>
{ dummyBarSubHeader }
{ this.getTenantSubHeaders() }
</Box>
);
}
//
// For children under TOP > ROLE/POLICY/RESOURCE
//
getChildrenListItems(treeList, type)
{
const { theme } = this.props;
if(undefined === treeList || !(treeList instanceof Array)){
return;
}
let _type = type;
return (
treeList.map( (item, pos) => { // eslint-disable-line no-unused-vars
let listItemKey = _type + '_' + item.path;
let isSelected = (undefined != item.selected ? true : false);
let isCollapseExpand = false;
if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){
isCollapseExpand = this.state.collapseExpands[listItemKey];
}
let expandIcon;
let collapseItem;
if(undefined !== item.children && (item.children instanceof Array) && 0 < item.children.length){
let childrenItem = this.getChildrenListItems(item.children, _type);
if(childrenItem){
if(isCollapseExpand){
expandIcon = (
<ListItemIcon
ex={ this.sxClasses.expandListItemIcon }
>
<ExpandLessIcon />
</ListItemIcon>
);
}else{
expandIcon = (
<ListItemIcon
sx={ this.sxClasses.expandListItemIcon }
>
<ExpandMoreIcon />
</ListItemIcon>
);
}
collapseItem = (
<Collapse
in={ isCollapseExpand }
{ ...theme.r3MainTree.collapse }
sx={ this.sxClasses.collapse }
>
<List
{ ...theme.r3MainTree.list }
>
{ childrenItem }
</List>
</Collapse>
);
}
}
let accordingItemText;
if(undefined !== item.selected && item.selected){
accordingItemText = theme.r3MainTree.childSelectedItemText;
}else{
accordingItemText = theme.r3MainTree.childItemText;
}
// [NOTE]
// Returning React.Fragment as a top component causes a key props error, so we use div to avoid it.
//
return (
<Box
key={ listItemKey }
>
<ListItem
onClick={ (event) => this.handleListItemChange(event, _type, item.path, isSelected, listItemKey) }
{ ...theme.r3MainTree.listItemButton }
>
<ListItemText
primary={ item.name }
{ ...accordingItemText }
sx={ this.sxClasses.childListItemText }
/>
{ expandIcon }
</ListItem>
{ collapseItem }
</Box>
);
})
);
}
//
// For Service Name(TOP) under TOP > SERVICE
//
getServicesListItems(treeList)
{
const { theme } = this.props;
if(undefined === treeList || !(treeList instanceof Array)){
return;
}
return (
treeList.map( (item, pos) => { // eslint-disable-line no-unused-vars
let listItemKey = 'service_' + item.path;
let isSelected = (undefined != item.selected ? true : false);
let isCollapseExpand = false;
if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){
isCollapseExpand = this.state.collapseExpands[listItemKey];
}
let ownerIcon;
if(r3IsSafeTypedEntity(item.owner, 'boolean') && true === item.owner){
ownerIcon = (
<ListItemIcon>
<OwnerIcon
{ ...theme.r3MainTree.editIcon }
/>
</ListItemIcon>
);
}
let expandIcon;
let collapseItem;
if(undefined !== item.children && (item.children instanceof Array) && 0 < item.children.length){
let childrenItem = this.getServiceMainListItems(item.children, item.path);
if(childrenItem){
if(isCollapseExpand){
expandIcon = (
<ListItemIcon
sx={ this.sxClasses.expandListItemIcon }
>
<ExpandLessIcon />
</ListItemIcon>
);
}else{
expandIcon = (
<ListItemIcon
sx={ this.sxClasses.expandListItemIcon }
>
<ExpandMoreIcon />
</ListItemIcon>
);
}
collapseItem = (
<Collapse
in={ isCollapseExpand }
{ ...theme.r3MainTree.collapse }
sx={ this.sxClasses.serviceLabelCollapse }
>
<List
{ ...theme.r3MainTree.list }
>
{ childrenItem }
</List>
</Collapse>
);
}
}
let accordingItemText;
if(undefined !== item.selected && item.selected){
accordingItemText = theme.r3MainTree.childSelectedItemText;
}else{
accordingItemText = theme.r3MainTree.childItemText;
}
// [NOTE]
// Returning React.Fragment as a top component causes a key props error, so we use div to avoid it.
//
return (
<Box
key={ listItemKey }
>
<ListItem
onClick={ (event) => this.handleNameItemInServiceChange(event, item.path, isSelected, listItemKey) }
{ ...theme.r3MainTree.listItemButton }
>
<ListItemText
primary={ item.name }
{ ...accordingItemText }
sx={ this.sxClasses.childListItemText }
/>
{ ownerIcon }
{ expandIcon }
</ListItem>
{ collapseItem }
</Box>
);
})
);
}
//
// For Type(ROLE/POLICY/RESOURCE) under TOP > SERVICE > Service Name(TOP)
//
// service_name : "service name"
//
getServiceMainListItems(treeList, service_name)
{
const { theme } = this.props;
if(undefined === treeList || !(treeList instanceof Array)){
return;
}
let _service_name = service_name;
return (
treeList.map( (item, pos) => { // eslint-disable-line no-unused-vars
let listItemKey = 'service_' + _service_name + '_' + item.path;
let isSelected = (undefined != item.selected ? true : false);
let isCollapseExpand = false;
if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){
isCollapseExpand = this.state.collapseExpands[listItemKey];
}
let expandIcon;
let collapseItem;
if(undefined !== item.children && (item.children instanceof Array) && 0 < item.children.length){
let childrenItem = this.getServiceChildrenListItems(item.children, _service_name, item.path);
if(childrenItem){
if(isCollapseExpand){
expandIcon = (
<ListItemIcon
sx={ this.sxClasses.expandListItemIcon }
>
<ExpandLessIcon />
</ListItemIcon>
);
}else{
expandIcon = (
<ListItemIcon
sx={ this.sxClasses.expandListItemIcon }
>
<ExpandMoreIcon />
</ListItemIcon>
);
}
collapseItem = (
<Collapse
in={ isCollapseExpand }
{ ...theme.r3MainTree.collapse }
sx={ this.sxClasses.collapse }
>
<List
{ ...theme.r3MainTree.list }
>
{ childrenItem }
</List>
</Collapse>
);
}
}
let accordingItemText;
if(undefined !== item.selected && item.selected){
accordingItemText = theme.r3MainTree.topSelectedItemText;
}else{
accordingItemText = theme.r3MainTree.topItemText;
}
// [NOTE]
// Returning React.Fragment as a top component causes a key props error, so we use div to avoid it.
//
return (
<Box
key={ listItemKey }
>
<ListItem
onClick={ (event) => this.handleTypeInServiceChange(event, _service_name, item.path, isSelected, listItemKey) }
{ ...theme.r3MainTree.listItemButton }
>
<ListItemIcon>
<LabelIcon />
</ListItemIcon>
<ListItemText
primary={ item.name }
{ ...accordingItemText }
sx={ this.sxClasses.inServiceLabelListItemText }
/>
{ expandIcon }
</ListItem>
{ collapseItem }
</Box>
);
})
);
}
//
// For Item under TOP > SERVICE > Service Name(TOP) > Type(ROLE/POLICY/RESOURCE)
//
// service_name : "service name"
// type_in_service : "type" under service
//
getServiceChildrenListItems(treeList, service_name, type_in_service)
{
const { theme } = this.props;
if(undefined === treeList || !(treeList instanceof Array)){
return;
}
let _service_name = service_name;
let _type_in_service = type_in_service;
return (
treeList.map( (item, pos) => { // eslint-disable-line no-unused-vars
let listItemKey = 'service_' + _service_name + '_' + _type_in_service + '_' + item.path;
let isSelected = (undefined != item.selected ? true : false);
let isCollapseExpand = false;
if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){
isCollapseExpand = this.state.collapseExpands[listItemKey];
}
let expandIcon;
let collapseItem;
if(undefined !== item.children && (item.children instanceof Array) && 0 < item.children.length){
let childrenItem = this.getServiceChildrenListItems(item.children, _service_name, _type_in_service);
if(childrenItem){
if(isCollapseExpand){
expandIcon = (
<ListItemIcon
sx={ this.sxClasses.expandListItemIcon }
>
<ExpandLessIcon />
</ListItemIcon>
);
}else{
expandIcon = (
<ListItemIcon
sx={ this.sxClasses.expandListItemIcon }
>
<ExpandMoreIcon />
</ListItemIcon>
);
}
collapseItem = (
<Collapse
in={ isCollapseExpand }
{ ...theme.r3MainTree.collapse }
sx={ this.sxClasses.collapse }
>
<List
{ ...theme.r3MainTree.list }
>
{ childrenItem }
</List>
</Collapse>
);
}
}
let accordingItemText;
if(undefined !== item.selected && item.selected){
accordingItemText = theme.r3MainTree.childSelectedItemText;
}else{
accordingItemText = theme.r3MainTree.childItemText;
}
// [NOTE]
// Returning React.Fragment as a top component causes a key props error, so we use div to avoid it.
//
return (
<Box
key={ listItemKey }
>
<ListItem
onClick={ (event) => this.handleListItemInServiceChange(event, _service_name, _type_in_service, item.path, isSelected, listItemKey) }
{ ...theme.r3MainTree.listItemButton }
>
<ListItemText
primary={ item.name }
{ ...accordingItemText }
sx={ this.sxClasses.inServiceChildListItemText }
/>
{ expandIcon }
</ListItem>
{ collapseItem }
</Box>
);
})
);
}
//
// For TOP
//
getMainListItems(treeList)
{
const { theme } = this.props;
console.info('CALL : getMainListItems');
if(undefined === treeList || !(treeList instanceof Array)){
return;
}
return (
treeList.map( (item, pos) => // eslint-disable-line no-unused-vars
{
let listItemKey = 'top_' + item.path;
let isSelected = (undefined != item.selected ? true : false);
let isCollapseExpand = false;
if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){
isCollapseExpand = this.state.collapseExpands[listItemKey];
}
let expandIcon;
let collapseItem;
if(r3IsSafeTypedEntity(item.children, 'array') && 0 < item.children.length){
let childrenItem;
if(r3CompareCaseString(serviceType, item.path)){
childrenItem = this.getServicesListItems(item.children);
}else{
childrenItem = this.getChildrenListItems(item.children, item.path);
}
if(childrenItem){
if(isCollapseExpand){
expandIcon = (
<ListItemIcon
sx={ this.sxClasses.expandListItemIcon }
>
<ExpandLessIcon />
</ListItemIcon>
);
}else{
expandIcon = (
<ListItemIcon
sx={ this.sxClasses.expandListItemIcon }
>
<ExpandMoreIcon />
</ListItemIcon>
);
}
collapseItem = (
<Collapse
in={ isCollapseExpand }
{ ...theme.r3MainTree.collapse }
>
<List
{ ...theme.r3MainTree.list }
>
{ childrenItem }
</List>
</Collapse>
);
}
}
let accordingItemText;
if(undefined !== item.selected && item.selected){
accordingItemText = theme.r3MainTree.topSelectedItemText;
}else{
accordingItemText = theme.r3MainTree.topItemText;
}
// [NOTE]
// Returning React.Fragment as a top component causes a key props error, so we use div to avoid it.
//
return (
<Box
key={ listItemKey }
>
<ListItem
onClick={ (event) => this.handleTypeItemChange(event, item.path, isSelected, listItemKey) }
{ ...theme.r3MainTree.listItemButton }
>
<ListItemIcon>
<LabelIcon />
</ListItemIcon>
<ListItemText
primary={ item.name }
{ ...accordingItemText }
sx={ this.sxClasses.listItemText }
/>
{ expandIcon }
</ListItem>
{ collapseItem }
</Box>
);
})
);
}
getPopupMessageDialog()
{
return (
<R3PopupMsgDialog
theme={ this.props.theme }
r3provider={ this.props.r3provider }
r3Message={ this.state.r3Message }
onClose={ this.handMessageDialogClose }
/>
);
}
getMainTreeLists()
{
const { theme } = this.props;
return (
<React.Fragment>
<Box
sx={ (this.props.isDocking ? this.sxClasses.dockedList : {}) }
>
{ this.getSubHeaders() }
<List
id={ componentKeyIds.topListId }
key={ componentKeyIds.topList }
{ ...theme.r3MainTree.list }
>
{ this.getMainListItems(this.props.treeList) }
</List>
{ this.getPopupMessageDialog() }
</Box>
<R3LocalTenantDialog
theme={ theme }
r3provider={ this.props.r3provider }
open={ this.state.localTenantDialogOpen }
userName={ this.props.userName }
createMode={ this.state.localTenantCreateMode }
allTenantNames={ this.getAllTenantNames() }
tenantName={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'name')) ? '' : this.props.selectedTenant.name }
tenantId={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'id')) ? '' : this.props.selectedTenant.id }
tenantDisplay={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'display')) ? '' : this.props.selectedTenant.display }
tenantDescription={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'description')) ? '' : this.props.selectedTenant.description }
tenantUsers={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'users') || !r3IsSafeTypedEntity(this.props.selectedTenant.users, 'array')) ? [ this.props.userName ] : r3DeepClone(this.props.selectedTenant.users).sort() }
onClose={ this.handLocalTenantDialogClose }
/>
</React.Fragment>
);
}
render()
{
const { theme } = this.props;
if(this.props.isDocking){
return this.getMainTreeLists();
}else{
// [NOTE]
// Since witdh=500 maybe used in the calculation as the initial value,
// it must be specified.(width=256 in default Theme does not work for me)
// And containerStyle must be specified.
//
return (
<React.Fragment>
<Drawer
open={ this.props.open }
onClose={ (event) => this.handleTreePopupClose(event) }
sx={ this.sxClasses.drawer }
>
<Box
sx={ this.sxClasses.adjustment }
>
{ this.getMainTreeLists() }
</Box>
</Drawer>
<R3LocalTenantDialog
theme={ theme }
r3provider={ this.props.r3provider }
open={ this.state.localTenantDialogOpen }
userName={ this.props.userName }
createMode={ this.state.localTenantCreateMode }
allTenantNames={ this.getAllTenantNames() }
tenantName={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'name')) ? '' : this.props.selectedTenant.name }
tenantId={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'id')) ? '' : this.props.selectedTenant.id }
tenantDisplay={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'display')) ? '' : this.props.selectedTenant.display }
tenantDescription={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'description')) ? '' : this.props.selectedTenant.description }
tenantUsers={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'users') || !r3IsSafeTypedEntity(this.props.selectedTenant.users, 'array')) ? [ this.props.userName ] : r3DeepClone(this.props.selectedTenant.users).sort() }
onClose={ this.handLocalTenantDialogClose }
/>
</React.Fragment>
);
}
}
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noexpandtab sw=4 ts=4 fdm=marker
* vim<600: noexpandtab sw=4 ts=4
*/