21720: implemented CustomStyleRulesCallback and replaced everywhere
[arvados.git] / services / workbench2 / src / views / virtual-machine-panel / virtual-machine-admin-panel.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
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';
19
20 type CssRules = 'moreOptionsButton' | 'moreOptions' | 'chipsRoot' | 'vmTableWrapper';
21
22 const styles: CustomStyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
23     moreOptionsButton: {
24         padding: 0
25     },
26     moreOptions: {
27         textAlign: 'right',
28         '&:last-child': {
29             paddingRight: 0
30         }
31     },
32     chipsRoot: {
33         margin: `0px -${theme.spacing.unit / 2}px`,
34     },
35     vmTableWrapper: {
36         overflowX: 'auto',
37     },
38 });
39
40 const mapStateToProps = (state: RootState) => {
41     return {
42         userUuid: state.auth.user!.uuid,
43         ...state.virtualMachines
44     };
45 };
46
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));
51     },
52     onAddLogin: (uuid: string) => {
53         dispatch<any>(openAddVirtualMachineLoginDialog(uuid));
54     },
55     onDeleteLogin: (uuid: string) => {
56         dispatch<any>(openRemoveVirtualMachineLoginDialog(uuid));
57     },
58     onLoginEdit: (uuid: string) => {
59         dispatch<any>(openEditVirtualMachineLoginDialog(uuid));
60     },
61 });
62
63 interface VirtualMachinesPanelDataProps {
64     virtualMachines: ListResults<any>;
65     logins: VirtualMachineLogins;
66     links: ListResults<any>;
67     userUuid: string;
68 }
69
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;
76 }
77
78 type VirtualMachineProps = VirtualMachinesPanelActionProps & VirtualMachinesPanelDataProps & WithStyles<CssRules>;
79
80 export const VirtualMachineAdminPanel = compose(
81     withStyles(styles),
82     connect(mapStateToProps, mapDispatchToProps))(
83         class extends React.Component<VirtualMachineProps> {
84             componentDidMount() {
85                 this.props.loadVirtualMachinesData();
86             }
87
88             render() {
89                 const { virtualMachines } = this.props;
90                 return (
91                     <Grid container spacing={16}>
92                         {virtualMachines.itemsAvailable > 0 && <CardContentWithVirtualMachines {...this.props} />}
93                     </Grid>
94                 );
95             }
96         }
97     );
98
99 const CardContentWithVirtualMachines = (props: VirtualMachineProps) =>
100     <Grid item xs={12}>
101         <Card>
102             <CardContent className={props.classes.vmTableWrapper}>
103                 {virtualMachinesTable(props)}
104             </CardContent>
105         </Card>
106     </Grid>;
107
108 const virtualMachinesTable = (props: VirtualMachineProps) =>
109     <Table data-cy="vm-admin-table">
110         <TableHead>
111             <TableRow>
112                 <TableCell>Uuid</TableCell>
113                 <TableCell>Host name</TableCell>
114                 <TableCell>Logins</TableCell>
115                 <TableCell />
116                 <TableCell />
117             </TableRow>
118         </TableHead>
119         <TableBody>
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>
124                     <TableCell>
125                         <Grid container spacing={8} className={props.classes.chipsRoot}>
126                             {props.links.items.filter((link) => (link.headUuid === machine.uuid)).map((permission, i) => (
127                                 <Grid item key={i}>
128                                     <Chip label={<VirtualMachineLogin linkUuid={permission.uuid} />} onDelete={event => props.onDeleteLogin(permission.uuid)} onClick={event => props.onLoginEdit(permission.uuid)} />
129                                 </Grid>
130                             ))}
131                         </Grid>
132                     </TableCell>
133                     <TableCell>
134                         <Tooltip title="Add Login Permission" disableFocusListener>
135                             <IconButton onClick={event => props.onAddLogin(machine.uuid)} className={props.classes.moreOptionsButton}>
136                                 <AddUserIcon />
137                             </IconButton>
138                         </Tooltip>
139                     </TableCell>
140                     <TableCell className={props.classes.moreOptions}>
141                         <Tooltip title="More options" disableFocusListener>
142                             <IconButton onClick={event => props.onOptionsMenuOpen(event, machine)} className={props.classes.moreOptionsButton}>
143                                 <MoreVerticalIcon />
144                             </IconButton>
145                         </Tooltip>
146                     </TableCell>
147                 </TableRow>
148             )}
149         </TableBody>
150     </Table>;