@@ -132,11 +132,9 @@ class OrganizationIntegrations extends AsyncComponent {
132
132
133
133
// Rendering
134
134
135
- renderBody ( ) {
136
- const { reloading, applications, appInstalls} = this . state ;
137
- const providers = this . providers
138
- . sort ( ( a , b ) => b . isInstalled - a . isInstalled )
139
- . map ( provider => (
135
+ renderProvider ( provider ) {
136
+ return (
137
+ < IntegrationRow key = { `row-${ provider . key } ` } >
140
138
< ProviderRow
141
139
key = { provider . key }
142
140
provider = { provider }
@@ -148,7 +146,55 @@ class OrganizationIntegrations extends AsyncComponent {
148
146
onReinstall = { this . onInstall }
149
147
enabledPlugins = { this . enabledPlugins }
150
148
/>
151
- ) ) ;
149
+ </ IntegrationRow >
150
+ ) ;
151
+ }
152
+
153
+ renderSentryApps ( apps , key ) {
154
+ const { appInstalls} = this . state ;
155
+
156
+ return (
157
+ < IntegrationRow key = { `row-${ key } ` } >
158
+ < SentryAppInstallations
159
+ key = { key }
160
+ orgId = { this . props . params . orgId }
161
+ installs = { appInstalls }
162
+ applications = { apps }
163
+ />
164
+ </ IntegrationRow >
165
+ ) ;
166
+ }
167
+
168
+ renderBody ( ) {
169
+ const { reloading, applications, appInstalls} = this . state ;
170
+
171
+ const installedProviders = this . providers
172
+ . filter ( p => p . isInstalled )
173
+ . map ( p => [ p . name , this . renderProvider ( p ) ] ) ;
174
+
175
+ const uninstalledProviders = this . providers
176
+ . filter ( p => ! p . isInstalled )
177
+ . map ( p => [ p . name , this . renderProvider ( p ) ] ) ;
178
+
179
+ const installedSentryApps = ( applications || [ ] )
180
+ . filter ( a => appInstalls . find ( i => i . app . slug === a . slug ) )
181
+ . map ( a => [ a . name , this . renderSentryApps ( [ a ] , a . slug ) ] ) ;
182
+
183
+ const uninstalledSentryApps = ( applications || [ ] )
184
+ . filter ( a => ! appInstalls . find ( i => i . app . slug === a . slug ) )
185
+ . map ( a => [ a . name , this . renderSentryApps ( [ a ] , a . slug ) ] ) ;
186
+
187
+ // Combine the list of Providers and Sentry Apps that have installations.
188
+ const installed = installedProviders
189
+ . concat ( installedSentryApps )
190
+ . sort ( ( a , b ) => a [ 0 ] . localeCompare ( b [ 0 ] ) )
191
+ . map ( i => i [ 1 ] ) ;
192
+
193
+ // Combine the list of Providers and Sentry Apps that have no installations.
194
+ const uninstalled = uninstalledProviders
195
+ . concat ( uninstalledSentryApps )
196
+ . sort ( ( a , b ) => a [ 0 ] . localeCompare ( b [ 0 ] ) )
197
+ . map ( i => i [ 1 ] ) ;
152
198
153
199
return (
154
200
< React . Fragment >
@@ -169,21 +215,17 @@ class OrganizationIntegrations extends AsyncComponent {
169
215
{ reloading && < StyledLoadingIndicator mini /> }
170
216
</ PanelHeader >
171
217
< PanelBody >
172
- { providers }
173
- { applications && (
174
- < SentryAppInstallations
175
- orgId = { this . props . params . orgId }
176
- installs = { appInstalls }
177
- applications = { applications }
178
- />
179
- ) }
218
+ { installed }
219
+ { uninstalled }
180
220
</ PanelBody >
181
221
</ Panel >
182
222
</ React . Fragment >
183
223
) ;
184
224
}
185
225
}
186
226
227
+ const IntegrationRow = styled . div `` ;
228
+
187
229
const StyledLoadingIndicator = styled ( LoadingIndicator ) `
188
230
position: absolute;
189
231
right: 7px;
0 commit comments