@@ -3,260 +3,54 @@ import styled from 'react-emotion';
3
3
import { browserHistory } from 'react-router' ;
4
4
import PropTypes from 'prop-types' ;
5
5
6
- import { t } from 'app/locale' ;
7
- import SentryTypes from 'app/sentryTypes' ;
8
- import LoadingIndicator from 'app/components/loadingIndicator' ;
6
+ import AsyncComponent from 'app/components/asyncComponent' ;
9
7
import Button from 'app/components/button' ;
10
- import DateTime from 'app/components/dateTime' ;
11
- import ErrorBoundary from 'app/components/errorBoundary' ;
12
- import ExternalLink from 'app/components/links/externalLink' ;
13
- import EventDataSection from 'app/components/events/eventDataSection' ;
14
- import EventDevice from 'app/components/events/device' ;
15
- import EventExtraData from 'app/components/events/extraData' ;
16
- import EventPackageData from 'app/components/events/packageData' ;
17
- import FileSize from 'app/components/fileSize' ;
18
- import NavTabs from 'app/components/navTabs' ;
19
- import NotFound from 'app/components/errors/notFound' ;
20
8
import withApi from 'app/utils/withApi' ;
21
9
import space from 'app/styles/space' ;
22
- import getDynamicText from 'app/utils/getDynamicText' ;
23
- import utils from 'app/utils' ;
24
- import { getMessage , getTitle } from 'app/utils/events' ;
25
10
26
- import { INTERFACES } from 'app/components/events/eventEntries' ;
27
- import TagsTable from './tagsTable' ;
11
+ import EventModalContent from './eventModalContent' ;
28
12
29
- const OTHER_SECTIONS = {
30
- context : EventExtraData ,
31
- packages : EventPackageData ,
32
- device : EventDevice ,
33
- } ;
34
-
35
- class EventDetails extends React . Component {
13
+ class EventDetails extends AsyncComponent {
36
14
static propTypes = {
37
- api : PropTypes . object ,
38
15
params : PropTypes . object ,
39
16
eventSlug : PropTypes . string . isRequired ,
40
17
} ;
41
18
42
- state = {
43
- loading : true ,
44
- error : false ,
45
- event : null ,
46
- activeTab : null ,
47
- } ;
19
+ getEndpoints ( ) {
20
+ const { orgId} = this . props . params ;
21
+ const [ projectId , eventId ] = this . props . eventSlug . toString ( ) . split ( ':' ) ;
48
22
49
- componentDidMount ( ) {
50
- this . fetchData ( ) ;
23
+ return [ [ 'event' , `/projects/${ orgId } /${ projectId } /events/${ eventId } /` ] ] ;
51
24
}
52
25
53
- componentDidUpdate ( prevProps ) {
54
- if ( prevProps . eventSlug != this . props . eventSlug ) {
55
- this . fetchData ( ) ;
56
- }
57
- }
58
-
59
- async fetchData ( ) {
60
- this . setState ( { loading : true , error : false } ) ;
61
- const { orgId} = this . props . params ;
62
- const [ projectId , eventId ] = this . props . eventSlug . split ( ':' ) ;
63
- try {
64
- if ( ! projectId || ! eventId ) {
65
- throw new Error ( 'Invalid eventSlug.' ) ;
66
- }
67
- const response = await this . props . api . requestPromise (
68
- `/projects/${ orgId } /${ projectId } /events/${ eventId } /`
69
- ) ;
70
- this . setState ( {
71
- activeTab : response . entries [ 0 ] . type ,
72
- event : response ,
73
- loading : false ,
74
- } ) ;
75
- } catch ( e ) {
76
- this . setState ( { error : true } ) ;
77
- }
26
+ onRequestSuccess ( { data} ) {
27
+ // Select the first interface as the active sub-tab
28
+ this . setState ( { activeTab : data . entries [ 0 ] . type } ) ;
78
29
}
79
30
80
31
handleClose = event => {
81
32
event . preventDefault ( ) ;
82
-
83
33
browserHistory . goBack ( ) ;
84
34
} ;
85
35
86
36
handleTabChange = tab => this . setState ( { activeTab : tab } ) ;
87
37
88
38
renderBody ( ) {
89
- if ( this . state . loading ) {
90
- return < LoadingIndicator /> ;
91
- }
92
- if ( this . state . error ) {
93
- return < NotFound /> ;
94
- }
95
- const { event, activeTab} = this . state ;
96
-
97
- return (
98
- < ColumnGrid >
99
- < ContentColumn >
100
- < EventHeader event = { this . state . event } />
101
- < NavTabs underlined = { true } >
102
- { event . entries . map ( entry => {
103
- if ( ! INTERFACES . hasOwnProperty ( entry . type ) ) {
104
- return null ;
105
- }
106
- const type = entry . type ;
107
- const classname = type === activeTab ? 'active' : null ;
108
- return (
109
- < li key = { type } className = { classname } >
110
- < a
111
- href = "#"
112
- onClick = { evt => {
113
- evt . preventDefault ( ) ;
114
- this . handleTabChange ( type ) ;
115
- } }
116
- >
117
- { utils . toTitleCase ( type ) }
118
- </ a >
119
- </ li >
120
- ) ;
121
- } ) }
122
- { Object . keys ( OTHER_SECTIONS ) . map ( section => {
123
- if ( utils . objectIsEmpty ( event [ section ] ) ) {
124
- return null ;
125
- }
126
- const classname = section === activeTab ? 'active' : null ;
127
- return (
128
- < li key = { section } className = { classname } >
129
- < a
130
- href = "#"
131
- onClick = { ( ) => {
132
- this . handleTabChange ( section ) ;
133
- } }
134
- >
135
- { utils . toTitleCase ( section ) }
136
- </ a >
137
- </ li >
138
- ) ;
139
- } ) }
140
- </ NavTabs >
141
- < ErrorBoundary message = { t ( 'Could not render event details' ) } >
142
- { this . renderActiveTab ( event , activeTab ) }
143
- </ ErrorBoundary >
144
- </ ContentColumn >
145
- < SidebarColumn >
146
- < EventMetadata event = { event } />
147
- < SidebarBlock >
148
- < TagsTable tags = { event . tags } />
149
- </ SidebarBlock >
150
- </ SidebarColumn >
151
- </ ColumnGrid >
152
- ) ;
153
- }
154
-
155
- renderActiveTab ( event , activeTab ) {
156
- const entry = event . entries . find ( item => item . type === activeTab ) ;
157
39
const [ projectId , _ ] = this . props . eventSlug . split ( ':' ) ;
158
- if ( INTERFACES [ activeTab ] ) {
159
- const Component = INTERFACES [ activeTab ] ;
160
- return (
161
- < Component
162
- projectId = { projectId }
163
- event = { event }
164
- type = { entry . type }
165
- data = { entry . data }
166
- isShare = { false }
167
- />
168
- ) ;
169
- } else if ( OTHER_SECTIONS [ activeTab ] ) {
170
- const Component = OTHER_SECTIONS [ activeTab ] ;
171
- return < Component event = { event } isShare = { false } /> ;
172
- } else {
173
- /*eslint no-console:0*/
174
- window . console &&
175
- console . error &&
176
- console . error ( 'Unregistered interface: ' + entry . type ) ;
177
-
178
- return (
179
- < EventDataSection event = { event } type = { entry . type } title = { entry . type } >
180
- < p > { t ( 'There was an error rendering this data.' ) } </ p >
181
- </ EventDataSection >
182
- ) ;
183
- }
184
- }
185
-
186
- render ( ) {
187
40
return (
188
41
< ModalContainer >
189
42
< CloseButton onClick = { this . handleClose } size = "zero" icon = "icon-close" />
190
- { this . renderBody ( ) }
43
+ < EventModalContent
44
+ onTabChange = { this . handleTabChange }
45
+ event = { this . state . event }
46
+ activeTab = { this . state . activeTab }
47
+ projectId = { projectId }
48
+ />
191
49
</ ModalContainer >
192
50
) ;
193
51
}
194
52
}
195
53
196
- const EventHeader = props => {
197
- const { title} = getTitle ( props . event ) ;
198
- return (
199
- < div >
200
- < h2 > { title } </ h2 >
201
- < p > { getMessage ( props . event ) } </ p >
202
- </ div >
203
- ) ;
204
- } ;
205
- EventHeader . propTypes = {
206
- event : SentryTypes . Event . isRequired ,
207
- } ;
208
-
209
- const EventMetadata = props => {
210
- const jsonUrl = 'TODO build this' ;
211
- const { event} = props ;
212
-
213
- return (
214
- < SidebarBlock withSeparator >
215
- < MetadataContainer > ID { event . eventID } </ MetadataContainer >
216
- < MetadataContainer >
217
- < DateTime
218
- date = { getDynamicText ( { value : event . dateCreated , fixed : 'Dummy timestamp' } ) }
219
- />
220
- < ExternalLink href = { jsonUrl } className = "json-link" >
221
- JSON (< FileSize bytes = { event . size } /> )
222
- </ ExternalLink >
223
- </ MetadataContainer >
224
- </ SidebarBlock >
225
- ) ;
226
- } ;
227
- EventMetadata . propTypes = {
228
- event : SentryTypes . Event . isRequired ,
229
- } ;
230
-
231
- const MetadataContainer = styled ( 'div' ) `
232
- display: flex;
233
- justify-content: space-between;
234
-
235
- color: ${ p => p . theme . gray3 } ;
236
- font-size: ${ p => p . theme . fontSizeMedium } ;
237
- ` ;
238
-
239
- const ColumnGrid = styled ( 'div' ) `
240
- display: grid;
241
- grid-template-columns: 70% 1fr;
242
- grid-template-rows: auto;
243
- grid-column-gap: ${ space ( 3 ) } ;
244
- ` ;
245
-
246
- const ContentColumn = styled ( 'div' ) `
247
- grid-column: 1 / 2;
248
- ` ;
249
-
250
- const SidebarColumn = styled ( 'div' ) `
251
- grid-column: 2 / 3;
252
- ` ;
253
-
254
- const SidebarBlock = styled ( 'div' ) `
255
- margin: 0 0 ${ space ( 2 ) } 0;
256
- padding: 0 0 ${ space ( 2 ) } 0;
257
- ${ p => ( p . withSeparator ? `border-bottom: 1px solid ${ p . theme . borderLight } ;` : '' ) }
258
- ` ;
259
-
260
54
const ModalContainer = styled ( 'div' ) `
261
55
position: absolute;
262
56
top: 0px;
0 commit comments