1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React from 'react';
6 import { connect } from 'react-redux';
7 import { Grid, Card, Chip, CardContent, TableBody, TableCell, TableHead, TableRow, Table, Tooltip, IconButton } from '@material-ui/core';
8 import { CustomStyleRulesCallback } from 'common/custom-theme';
9 import { WithStyles, withStyles } from '@material-ui/core/styles';
10 import { ArvadosTheme } from 'common/custom-theme';
11 import { compose, Dispatch } from 'redux';
12 import { loadVirtualMachinesAdminData, openAddVirtualMachineLoginDialog, openRemoveVirtualMachineLoginDialog, openEditVirtualMachineLoginDialog } from 'store/virtual-machines/virtual-machines-actions';
13 import { RootState } from 'store/store';
14 import { ListResults } from 'services/common-service/common-service';
15 import { MoreVerticalIcon, AddUserIcon } from 'components/icon/icon';
16 import { VirtualMachineLogins, VirtualMachinesResource } from 'models/virtual-machines';
17 import { openVirtualMachinesContextMenu } from 'store/context-menu/context-menu-actions';
18 import { ResourceUuid, VirtualMachineHostname, VirtualMachineLogin } from 'views-components/data-explorer/renderers';
20 type CssRules = 'moreOptionsButton' | 'moreOptions' | 'chipsRoot' | 'vmTableWrapper';
22 const styles: CustomStyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
33 margin: `0px -${theme.spacing.unit / 2}px`,
40 const mapStateToProps = (state: RootState) => {
42 userUuid: state.auth.user!.uuid,
43 ...state.virtualMachines
47 const mapDispatchToProps = (dispatch: Dispatch): Pick<VirtualMachinesPanelActionProps, 'loadVirtualMachinesData' | 'onOptionsMenuOpen' | 'onAddLogin' | 'onDeleteLogin' | 'onLoginEdit'> => ({
48 loadVirtualMachinesData: () => dispatch<any>(loadVirtualMachinesAdminData()),
49 onOptionsMenuOpen: (event, virtualMachine) => {
50 dispatch<any>(openVirtualMachinesContextMenu(event, virtualMachine));
52 onAddLogin: (uuid: string) => {
53 dispatch<any>(openAddVirtualMachineLoginDialog(uuid));
55 onDeleteLogin: (uuid: string) => {
56 dispatch<any>(openRemoveVirtualMachineLoginDialog(uuid));
58 onLoginEdit: (uuid: string) => {
59 dispatch<any>(openEditVirtualMachineLoginDialog(uuid));
63 interface VirtualMachinesPanelDataProps {
64 virtualMachines: ListResults<any>;
65 logins: VirtualMachineLogins;
66 links: ListResults<any>;
70 interface VirtualMachinesPanelActionProps {
71 loadVirtualMachinesData: () => string;
72 onOptionsMenuOpen: (event: React.MouseEvent<HTMLElement>, virtualMachine: VirtualMachinesResource) => void;
73 onAddLogin: (uuid: string) => void;
74 onDeleteLogin: (uuid: string) => void;
75 onLoginEdit: (uuid: string) => void;
78 type VirtualMachineProps = VirtualMachinesPanelActionProps & VirtualMachinesPanelDataProps & WithStyles<CssRules>;
80 export const VirtualMachineAdminPanel = compose(
82 connect(mapStateToProps, mapDispatchToProps))(
83 class extends React.Component<VirtualMachineProps> {
85 this.props.loadVirtualMachinesData();
89 const { virtualMachines } = this.props;
91 <Grid container spacing={16}>
92 {virtualMachines.itemsAvailable > 0 && <CardContentWithVirtualMachines {...this.props} />}
99 const CardContentWithVirtualMachines = (props: VirtualMachineProps) =>
102 <CardContent className={props.classes.vmTableWrapper}>
103 {virtualMachinesTable(props)}
108 const virtualMachinesTable = (props: VirtualMachineProps) =>
109 <Table data-cy="vm-admin-table">
112 <TableCell>Uuid</TableCell>
113 <TableCell>Host name</TableCell>
114 <TableCell>Logins</TableCell>
120 {props.virtualMachines.items.map((machine, index) =>
121 <TableRow key={index}>
122 <TableCell><ResourceUuid uuid={machine.uuid} /></TableCell>
123 <TableCell><VirtualMachineHostname uuid={machine.uuid} /></TableCell>
125 <Grid container spacing={8} className={props.classes.chipsRoot}>
126 {props.links.items.filter((link) => (link.headUuid === machine.uuid)).map((permission, i) => (
128 <Chip label={<VirtualMachineLogin linkUuid={permission.uuid} />} onDelete={event => props.onDeleteLogin(permission.uuid)} onClick={event => props.onLoginEdit(permission.uuid)} />
134 <Tooltip title="Add Login Permission" disableFocusListener>
135 <IconButton onClick={event => props.onAddLogin(machine.uuid)} className={props.classes.moreOptionsButton}>
140 <TableCell className={props.classes.moreOptions}>
141 <Tooltip title="More options" disableFocusListener>
142 <IconButton onClick={event => props.onOptionsMenuOpen(event, machine)} className={props.classes.moreOptionsButton}>