Skip to content

Commit c172a4d

Browse files
ref(grouping-ui): Add UI improvements (#26627)
1 parent eff3c47 commit c172a4d

File tree

2 files changed

+60
-70
lines changed

2 files changed

+60
-70
lines changed

static/app/views/organizationGroupDetails/grouping/grouping.tsx

Lines changed: 50 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {Fragment, useEffect, useState} from 'react';
1+
import {useEffect, useState} from 'react';
22
import styled from '@emotion/styled';
33
import {Location} from 'history';
4+
import debounce from 'lodash/debounce';
45

56
import {Client} from 'app/api';
67
import Button from 'app/components/button';
@@ -10,6 +11,7 @@ import ListItem from 'app/components/list/listItem';
1011
import LoadingError from 'app/components/loadingError';
1112
import LoadingIndicator from 'app/components/loadingIndicator';
1213
import Pagination from 'app/components/pagination';
14+
import {DEFAULT_DEBOUNCE_DURATION} from 'app/constants';
1315
import {IconFlag} from 'app/icons';
1416
import {t, tct} from 'app/locale';
1517
import space from 'app/styles/space';
@@ -30,10 +32,16 @@ type Props = {
3032
api: Client;
3133
};
3234

35+
type ErrorCode = 'not_hierarchical' | 'no_events' | 'merged_issues' | 'missing_feature';
36+
3337
type Error = {
3438
status: number;
3539
responseJSON?: {
36-
detail: string;
40+
detail: {
41+
code: ErrorCode;
42+
extra: Record<string, any>;
43+
message: string;
44+
};
3745
};
3846
};
3947

@@ -76,6 +84,10 @@ function Grouping({api, groupId, location}: Props) {
7684
fetchGroupingLevelDetails();
7785
}, [activeGroupingLevel, location.query]);
7886

87+
const handleSetActiveGroupingLevel = debounce((groupingLevelId: string) => {
88+
setActiveGroupingLevel(Number(groupingLevelId));
89+
}, DEFAULT_DEBOUNCE_DURATION);
90+
7991
async function fetchGroupingLevels() {
8092
setIsLoading(true);
8193
setError(undefined);
@@ -135,19 +147,23 @@ function Grouping({api, groupId, location}: Props) {
135147

136148
if (error) {
137149
if (error.status === 403 && error.responseJSON?.detail) {
150+
const {message, code} = error.responseJSON.detail;
138151
return (
139152
<Wrapper>
140153
<EmptyMessage
154+
size="large"
141155
icon={<IconFlag size="xl" />}
142156
action={
143-
<Button
144-
to={`/organizations/sentry/issues/${groupId}/merged/?${location.search}`}
145-
>
146-
{t('Unmerge issue')}
147-
</Button>
157+
code === 'merged_issues' ? (
158+
<Button
159+
to={`/organizations/sentry/issues/${groupId}/merged/?${location.search}`}
160+
>
161+
{t('Unmerge issue')}
162+
</Button>
163+
) : undefined
148164
}
149165
>
150-
{error.responseJSON.detail}
166+
{message}
151167
</EmptyMessage>
152168
</Wrapper>
153169
);
@@ -169,10 +185,6 @@ function Grouping({api, groupId, location}: Props) {
169185
);
170186
}
171187

172-
//function handleRegroup() {
173-
// Todo(Priscila): Implement it
174-
//}
175-
176188
const links = parseLinkHeader(pagination);
177189
const hasMore = links.previous?.results || links.next?.results;
178190

@@ -196,59 +208,37 @@ function Grouping({api, groupId, location}: Props) {
196208
return value === 0 ? t('Automatically grouped') : t('Level %s', value);
197209
}}
198210
value={activeGroupingLevel ?? 0}
199-
onChange={groupingLevelId =>
200-
setActiveGroupingLevel(Number(groupingLevelId))
201-
}
211+
onChange={handleSetActiveGroupingLevel}
202212
/>
203213
</StyledListItem>
204214
<StyledListItem>
205-
{isGroupingLevelDetailsLoading ? (
206-
<div>
207-
<div>{t('What happens to this issue')}</div>
208-
<LoadingIndicator mini />
209-
</div>
210-
) : (
211-
<Fragment>
212-
<div>
213-
{t('What happens to this issue')}
214-
<WhatHappensDescription>
215-
{tct(
216-
`This issue will be deleted and [quantity] new issues will be created.`,
217-
{
218-
quantity: hasMore
219-
? `${activeGroupingLevelDetails.length}+`
220-
: activeGroupingLevelDetails.length,
221-
}
222-
)}
223-
</WhatHappensDescription>
224-
</div>
225-
<NewIssues>
226-
{activeGroupingLevelDetails.map(activeGroupingLevelDetail => (
227-
<NewIssue
228-
key={activeGroupingLevelDetail.hash}
229-
sampleEvent={activeGroupingLevelDetail.latestEvent}
230-
eventCount={activeGroupingLevelDetail.eventCount}
231-
/>
232-
))}
233-
</NewIssues>
234-
</Fragment>
235-
)}
215+
<div>
216+
{t('What happens to this issue')}
217+
<WhatHappensDescription>
218+
{tct(
219+
`This issue will be deleted and [quantity] new issues will be created.`,
220+
{
221+
quantity: hasMore
222+
? `${activeGroupingLevelDetails.length}+`
223+
: activeGroupingLevelDetails.length,
224+
}
225+
)}
226+
</WhatHappensDescription>
227+
</div>
228+
<NewIssues>
229+
{activeGroupingLevelDetails.map(({hash, latestEvent, eventCount}) => (
230+
<NewIssue
231+
key={hash}
232+
sampleEvent={latestEvent}
233+
eventCount={eventCount}
234+
isReloading={isGroupingLevelDetailsLoading}
235+
/>
236+
))}
237+
</NewIssues>
236238
</StyledListItem>
237239
</StyledList>
238240
<Pagination pageLinks={pagination} />
239241
</div>
240-
<Action>
241-
<Button
242-
priority="primary"
243-
disabled={
244-
isGroupingLevelDetailsLoading ||
245-
!activeGroupingLevel ||
246-
activeGroupingLevel === 0
247-
}
248-
>
249-
{t('Regroup')}
250-
</Button>
251-
</Action>
252242
</Wrapper>
253243
);
254244
}
@@ -258,6 +248,7 @@ export default withApi(Grouping);
258248
const Wrapper = styled('div')`
259249
flex: 1;
260250
display: grid;
251+
align-content: flex-start;
261252
background: ${p => p.theme.background};
262253
grid-gap: ${space(2)};
263254
margin: -${space(3)} -${space(4)};
@@ -268,21 +259,13 @@ const Description = styled('p')`
268259
margin-bottom: ${space(0.5)};
269260
`;
270261

271-
const Action = styled('div')`
272-
border-top: 1px solid ${p => p.theme.border};
273-
display: flex;
274-
justify-content: flex-end;
275-
padding: ${space(2)} 0 0;
276-
margin-top: ${space(1)};
277-
`;
278-
279262
const StyledListItem = styled(ListItem)`
280263
display: grid;
281264
grid-gap: ${space(1.5)};
282265
`;
283266

284267
const StyledRangeSlider = styled(RangeSlider)`
285-
max-width: 20%;
268+
max-width: 300px;
286269
`;
287270

288271
const StyledList = styled(List)`

static/app/views/organizationGroupDetails/grouping/newIssue.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import {Event} from 'app/types/event';
88
type Props = {
99
sampleEvent: Event;
1010
eventCount: number;
11+
isReloading: boolean;
1112
};
1213

13-
function NewIssue({sampleEvent, eventCount}: Props) {
14+
function NewIssue({sampleEvent, eventCount, isReloading}: Props) {
1415
const {title, culprit} = sampleEvent;
1516
return (
16-
<StyledCard interactive={false}>
17+
<StyledCard interactive={false} isReloading={isReloading}>
1718
<div>
1819
<Title>{title}</Title>
1920
<CulPrint>{culprit}</CulPrint>
@@ -28,7 +29,7 @@ function NewIssue({sampleEvent, eventCount}: Props) {
2829

2930
export default NewIssue;
3031

31-
const StyledCard = styled(Card)`
32+
const StyledCard = styled(Card)<{isReloading: boolean}>`
3233
margin-bottom: -1px;
3334
overflow: hidden;
3435
display: grid;
@@ -37,6 +38,12 @@ const StyledCard = styled(Card)`
3738
padding: ${space(1.5)} ${space(2)};
3839
grid-gap: ${space(2)};
3940
word-break: break-word;
41+
${p =>
42+
p.isReloading &&
43+
`
44+
opacity: 0.5;
45+
pointer-events: none;
46+
`}
4047
`;
4148

4249
const Title = styled('div')`

0 commit comments

Comments
 (0)