Merge branch 'master' into 14100-process-logs-service
[arvados-workbench2.git] / src / views-components / details-panel / details-panel.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import { Drawer, IconButton, Tabs, Tab, Typography, Grid } from '@material-ui/core';
7 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
8 import { ArvadosTheme } from '~/common/custom-theme';
9 import * as classnames from "classnames";
10 import { connect } from 'react-redux';
11 import { RootState } from '~/store/store';
12 import { detailsPanelActions } from "~/store/details-panel/details-panel-action";
13 import { CloseIcon } from '~/components/icon/icon';
14 import { EmptyResource } from '~/models/empty';
15 import { Dispatch } from "redux";
16 import { ResourceKind } from "~/models/resource";
17 import { ProjectDetails } from "./project-details";
18 import { CollectionDetails } from "./collection-details";
19 import { ProcessDetails } from "./process-details";
20 import { EmptyDetails } from "./empty-details";
21 import { DetailsData } from "./details-data";
22 import { DetailsResource } from "~/models/details";
23 import { getResource } from '../../store/resources/resources';
24
25 type CssRules = 'drawerPaper' | 'container' | 'opened' | 'headerContainer' | 'headerIcon' | 'headerTitle' | 'tabContainer';
26
27 const drawerWidth = 320;
28 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
29     container: {
30         width: 0,
31         position: 'relative',
32         height: 'auto',
33         transition: 'width 0.5s ease',
34         '&$opened': {
35             width: drawerWidth
36         }
37     },
38     opened: {},
39     drawerPaper: {
40         position: 'relative',
41         width: drawerWidth
42     },
43     headerContainer: {
44         color: theme.palette.grey["600"],
45         margin: `${theme.spacing.unit}px 0`,
46         textAlign: 'center'
47     },
48     headerIcon: {
49         fontSize: '2.125rem'
50     },
51     headerTitle: {
52         overflowWrap: 'break-word',
53         wordWrap: 'break-word'
54     },
55     tabContainer: {
56         padding: theme.spacing.unit * 3
57     }
58 });
59
60 const getItem = (resource: DetailsResource): DetailsData => {
61     const res = resource || { kind: undefined, name: 'Projects' };
62     switch (res.kind) {
63         case ResourceKind.PROJECT:
64             return new ProjectDetails(res);
65         case ResourceKind.COLLECTION:
66             return new CollectionDetails(res);
67         case ResourceKind.PROCESS:
68             return new ProcessDetails(res);
69         default:
70             return new EmptyDetails(res as EmptyResource);
71     }
72 };
73
74 const mapStateToProps = ({ detailsPanel, resources }: RootState) => {
75     const resource = getResource(detailsPanel.resourceUuid)(resources) as DetailsResource;
76     return {
77         isOpened: detailsPanel.isOpened,
78         item: getItem(resource)
79     };
80 };
81
82 const mapDispatchToProps = (dispatch: Dispatch) => ({
83     onCloseDrawer: () => {
84         dispatch(detailsPanelActions.TOGGLE_DETAILS_PANEL());
85     }
86 });
87
88 export interface DetailsPanelDataProps {
89     onCloseDrawer: () => void;
90     isOpened: boolean;
91     item: DetailsData;
92 }
93
94 type DetailsPanelProps = DetailsPanelDataProps & WithStyles<CssRules>;
95
96 export const DetailsPanel = withStyles(styles)(
97     connect(mapStateToProps, mapDispatchToProps)(
98         class extends React.Component<DetailsPanelProps> {
99             state = {
100                 tabsValue: 0
101             };
102
103             handleChange = (event: any, value: boolean) => {
104                 this.setState({ tabsValue: value });
105             }
106
107             renderTabContainer = (children: React.ReactElement<any>) =>
108                 <Typography className={this.props.classes.tabContainer} component="div">
109                     {children}
110                 </Typography>
111
112             render() {
113                 const { classes, onCloseDrawer, isOpened, item } = this.props;
114                 const { tabsValue } = this.state;
115                 return (
116                     <Typography component="div"
117                         className={classnames([classes.container, { [classes.opened]: isOpened }])}>
118                         <Drawer variant="permanent" anchor="right" classes={{ paper: classes.drawerPaper }}>
119                             <Typography component="div" className={classes.headerContainer}>
120                                 <Grid container alignItems='center' justify='space-around'>
121                                     <Grid item xs={2}>
122                                         {item.getIcon(classes.headerIcon)}
123                                     </Grid>
124                                     <Grid item xs={8}>
125                                         <Typography variant="title" className={classes.headerTitle}>
126                                             {item.getTitle()}
127                                         </Typography>
128                                     </Grid>
129                                     <Grid item>
130                                         <IconButton color="inherit" onClick={onCloseDrawer}>
131                                             {<CloseIcon />}
132                                         </IconButton>
133                                     </Grid>
134                                 </Grid>
135                             </Typography>
136                             <Tabs value={tabsValue} onChange={this.handleChange}>
137                                 <Tab disableRipple label="Details" />
138                                 <Tab disableRipple label="Activity" disabled />
139                             </Tabs>
140                             {tabsValue === 0 && this.renderTabContainer(
141                                 <Grid container direction="column">
142                                     {item.getDetails()}
143                                 </Grid>
144                             )}
145                             {tabsValue === 1 && this.renderTabContainer(
146                                 <Grid container direction="column" />
147                             )}
148                         </Drawer>
149                     </Typography>
150                 );
151             }
152         }
153     )
154 );