1
- import { Icon , makeStyles , Theme } from "@material-ui/core" ;
1
+ import { Icon , makeStyles } from "@material-ui/core" ;
2
2
import Card from "@material-ui/core/Card" ;
3
3
import CardContent from "@material-ui/core/CardContent" ;
4
4
import Grid from "@material-ui/core/Grid" ;
@@ -12,7 +12,13 @@ import {
12
12
expandIcon ,
13
13
readyIcon ,
14
14
syncingIcon ,
15
+ downloadIcon ,
15
16
} from "../../common/utils/svgIcons" ;
17
+ import ButtonBase from "@material-ui/core/ButtonBase" ;
18
+ import api from "../../api" ;
19
+ import { formatDateTimeForFilename } from "../../common/utils/dateUtil" ;
20
+ import { Subject } from "rxjs" ;
21
+ import Snackbar from "../../common/components/data-display/feedback/Snackbar" ;
16
22
17
23
export type OverviewItemProps = {
18
24
status : Status ;
@@ -21,14 +27,14 @@ export type OverviewItemProps = {
21
27
} ;
22
28
23
29
const getCardHeaderBackgroundImage = ( props : OverviewItemProps ) => {
24
- if ( props . status . status . includes ( "Ready" ) ) {
30
+ if ( isServiceReady ( props . status ) ) {
25
31
return "linear-gradient(to right, #1f995a, #00e64d)" ;
26
32
} else {
27
33
return "linear-gradient(to left, #fea900, #f15a24 0%)" ;
28
34
}
29
35
} ;
30
36
31
- const useStyles = makeStyles ( ( theme : Theme ) => ( {
37
+ const useStyles = makeStyles ( ( ) => ( {
32
38
cardHeader : ( props : OverviewItemProps ) => ( {
33
39
padding : "12px 16px 12px 24px" ,
34
40
backgroundImage : getCardHeaderBackgroundImage ( props ) ,
@@ -56,19 +62,6 @@ const useStyles = makeStyles((theme: Theme) => ({
56
62
cardContentStatusValueText : {
57
63
color : "#f2f2f2" ,
58
64
} ,
59
- statusDot : {
60
- height : 10 ,
61
- width : 10 ,
62
- borderRadius : "50%" ,
63
- display : "inline-block" ,
64
- marginRight : 10 ,
65
- } ,
66
- active : {
67
- backgroundColor : theme . palette . success . light ,
68
- } ,
69
- inactive : {
70
- backgroundColor : theme . palette . error . light ,
71
- } ,
72
65
expandIcon : {
73
66
cursor : "pointer" ,
74
67
} ,
@@ -78,12 +71,28 @@ const useStyles = makeStyles((theme: Theme) => ({
78
71
statusIcon : {
79
72
padding : "0 10px" ,
80
73
} ,
74
+ downloadIconContainer : {
75
+ fontSize : "14px" ,
76
+ color : "#000000" ,
77
+ textDecoration : "underline" ,
78
+ } ,
79
+ detailsIconContainer : {
80
+ display : "flex" ,
81
+ justifyContent : "flex-end" ,
82
+ } ,
83
+ titleAndLogsContainer : {
84
+ width : "66.6%" ,
85
+ display : "flex" ,
86
+ justifyContent : "space-between" ,
87
+ alignItems : "center" ,
88
+ } ,
81
89
} ) ) ;
82
90
83
91
const OverviewItem = ( props : OverviewItemProps ) : ReactElement => {
84
92
const { status, opendexdLocked, opendexdNotReady } = props ;
85
93
const [ detailsOpen , setDetailsOpen ] = useState ( false ) ;
86
94
const classes = useStyles ( props ) ;
95
+ const errorMsgOpenSubject = new Subject < boolean > ( ) ;
87
96
88
97
const isDetailsIconVisible = ( status : Status ) : boolean => {
89
98
return (
@@ -95,28 +104,72 @@ const OverviewItem = (props: OverviewItemProps): ReactElement => {
95
104
} ;
96
105
97
106
const getStatusIcon = ( status : Status ) => {
98
- if ( status . status . includes ( "Ready" ) ) {
107
+ if ( isServiceReady ( status ) ) {
99
108
return readyIcon ;
100
109
} else {
101
110
return syncingIcon ;
102
111
}
103
112
} ;
104
113
114
+ const isDownloadLogsEnabled = ( status : Status ) : boolean => {
115
+ return (
116
+ ! status . status . includes ( "light mode" ) && status . status !== "Disabled"
117
+ ) ;
118
+ } ;
119
+
120
+ const downloadLogs = ( serviceName : string , handleError : ( ) => void ) : void => {
121
+ api . logs$ ( serviceName ) . subscribe ( {
122
+ next : ( resp : string ) => {
123
+ const blob = new Blob ( [ resp ] ) ;
124
+ const url = URL . createObjectURL ( blob ) ;
125
+ const anchor = Object . assign ( document . createElement ( "a" ) , {
126
+ href : url ,
127
+ download : `${ serviceName } _${ formatDateTimeForFilename (
128
+ new Date ( )
129
+ ) } .log`,
130
+ style : { display : "none" } ,
131
+ } ) ;
132
+ anchor . click ( ) ;
133
+ } ,
134
+ error : handleError ,
135
+ } ) ;
136
+ } ;
137
+
105
138
return (
106
139
< div >
107
140
< Card square = { true } >
108
141
< Grid container className = { classes . cardHeader } >
109
- < Grid >
110
- < Typography
111
- component = "span"
112
- variant = "body1"
113
- className = { classes . cardHeading }
114
- >
115
- { props . status . service }
116
- </ Typography >
117
- </ Grid >
142
+ < div className = { classes . titleAndLogsContainer } >
143
+ < Grid >
144
+ < Typography
145
+ component = "span"
146
+ variant = "body1"
147
+ className = { classes . cardHeading }
148
+ >
149
+ { props . status . service }
150
+ </ Typography >
151
+ </ Grid >
118
152
119
- < Grid >
153
+ < Grid >
154
+ { isDownloadLogsEnabled ( status ) && (
155
+ < div >
156
+ < ButtonBase
157
+ className = { classes . downloadIconContainer }
158
+ onClick = { ( ) =>
159
+ downloadLogs ( status . service , ( ) =>
160
+ errorMsgOpenSubject ?. next ( true )
161
+ )
162
+ }
163
+ >
164
+ < img src = { downloadIcon } alt = "download logs" />
165
+ Download Logs
166
+ </ ButtonBase >
167
+ </ div >
168
+ ) }
169
+ </ Grid >
170
+ </ div >
171
+
172
+ < Grid className = { classes . detailsIconContainer } >
120
173
{ isDetailsIconVisible ( status ) && (
121
174
< Icon
122
175
onClick = { ( ) => setDetailsOpen ( true ) }
@@ -154,6 +207,12 @@ const OverviewItem = (props: OverviewItemProps): ReactElement => {
154
207
titleBackground = { getCardHeaderBackgroundImage ( props ) }
155
208
/>
156
209
) }
210
+
211
+ < Snackbar
212
+ message = { `Could not download the logs for ${ status . service } ` }
213
+ openSubject = { errorMsgOpenSubject }
214
+ type = "error"
215
+ > </ Snackbar >
157
216
</ div >
158
217
) ;
159
218
} ;
0 commit comments