Skip to content

Commit 2d5a9b1

Browse files
authored
Merges #906 Closes #866
2 parents 51f29eb + fcb57d0 commit 2d5a9b1

13 files changed

+5382
-3282
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
title: Improve loading and error indicators
3+
category: added
4+
author: Eva Millán <[email protected]>
5+
issue: 866
6+
notes: >
7+
Added error and loading messages to improve the user experience
8+
when the data takes time to load.
9+

ui/src/apollo/queries.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,4 +494,6 @@ export {
494494
getOrganization,
495495
getScheduledTasks,
496496
findOrganization,
497+
GET_INDIVIDUAL_BYUUID,
498+
GET_ORGANIZATION,
497499
};

ui/src/components/IndividualsTable.stories.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,3 +590,50 @@ export const HiddenHeader = () => ({
590590
isExpandable: false,
591591
}),
592592
});
593+
594+
export const OnError = () => ({
595+
components: { IndividualsTable },
596+
template: IndividualsTableTemplate,
597+
methods: {
598+
queryIndividuals() {
599+
throw new Error("Test error message");
600+
},
601+
deleteIndividual() {
602+
return true;
603+
},
604+
getCountries() {
605+
return true;
606+
},
607+
recommendMatches() {
608+
return {
609+
data: {
610+
recommendMatches: {
611+
jobId: "b65d2170-a560-4b20-954e-fc8c9f5afdd4",
612+
},
613+
},
614+
};
615+
},
616+
},
617+
provide: () => ({
618+
getRecommendations: () => {},
619+
getRecommendationsCount: () => {
620+
return {
621+
data: {
622+
recommendedMerge: {
623+
pageInfo: {
624+
totalResults: 0,
625+
},
626+
},
627+
},
628+
};
629+
},
630+
manageRecommendation: () => {},
631+
}),
632+
data: () => ({
633+
query: query,
634+
hideHeader: false,
635+
outlined: true,
636+
isExpandable: false,
637+
}),
638+
});
639+

ui/src/components/IndividualsTable.vue

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@
204204
</v-col>
205205
</div>
206206
</template>
207+
<template v-slot:no-data>
208+
<v-alert v-if="error" class="text-left" density="compact" type="error">
209+
{{ error }}
210+
</v-alert>
211+
<p v-else-if="Object.keys(filters).length > 0">
212+
No results matched your search.
213+
</p>
214+
<p v-else>No data available</p>
215+
</template>
207216
</v-data-table-server>
208217

209218
<v-dialog v-model="dialog.open" max-width="500px">
@@ -447,6 +456,7 @@ export default {
447456
uuid: "",
448457
},
449458
selected: [],
459+
error: null,
450460
};
451461
},
452462
computed: {
@@ -472,24 +482,30 @@ export default {
472482
) {
473483
if (this.disabledSearch) return;
474484
this.loading = true;
475-
let response = await this.fetchPage(
476-
page,
477-
this.itemsPerPage,
478-
filters,
479-
orderBy
480-
);
481-
if (response) {
482-
this.individuals = formatIndividuals(
483-
response.data.individuals.entities
485+
this.error = null;
486+
try {
487+
let response = await this.fetchPage(
488+
page,
489+
this.itemsPerPage,
490+
filters,
491+
orderBy
484492
);
485-
this.pageCount = response.data.individuals.pageInfo.numPages;
486-
this.page = response.data.individuals.pageInfo.page;
487-
this.totalResults = response.data.individuals.pageInfo.totalResults;
488-
this.allSelected = false;
489-
this.$emit("updateIndividuals", this.individuals);
493+
if (response) {
494+
this.individuals = formatIndividuals(
495+
response.data.individuals.entities
496+
);
497+
this.pageCount = response.data.individuals.pageInfo.numPages;
498+
this.page = response.data.individuals.pageInfo.page;
499+
this.totalResults = response.data.individuals.pageInfo.totalResults;
500+
this.allSelected = false;
501+
this.$emit("updateIndividuals", this.individuals);
502+
}
503+
} catch (error) {
504+
this.error = this.$getErrorMessage(error);
505+
} finally {
506+
this.loading = false;
507+
this.selected = [];
490508
}
491-
this.loading = false;
492-
this.selected = [];
493509
},
494510
startDrag(item, isSelected, toggleSelect, event) {
495511
if (!isSelected(item)) {

ui/src/components/JobsTable.stories.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,27 @@ export const Empty = () => ({
117117
},
118118
}),
119119
});
120+
121+
export const OnError = () => ({
122+
components: { JobsTable },
123+
template: JobsTableTemplate,
124+
methods: {
125+
getJobs() {
126+
throw new Error("Test error message")
127+
},
128+
},
129+
data: () => ({
130+
query: {
131+
data: {
132+
jobs: {
133+
entities: [],
134+
pageInfo: {
135+
page: 1,
136+
numPages: 1,
137+
totalResults: 0,
138+
},
139+
},
140+
},
141+
},
142+
}),
143+
});

ui/src/components/JobsTable.vue

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
Add
1616
</v-btn>
1717
</header>
18-
<v-table v-if="jobs.length > 0">
18+
<v-alert v-if="error" :text="error" density="compact" type="error" />
19+
<v-progress-linear v-if="isLoading" color="primary" indeterminate />
20+
<v-table v-else-if="jobs.length > 0">
1921
<template v-slot:default>
2022
<thead>
2123
<tr>
@@ -89,18 +91,31 @@ export default {
8991
pageSize: 10,
9092
pageCount: 1,
9193
openModal: false,
94+
error: null,
95+
isLoading: false,
9296
};
9397
},
9498
created() {
9599
this.getPaginatedJobs(1);
96100
},
97101
methods: {
98102
async getPaginatedJobs(page = this.page, pageSize = this.pageSize) {
99-
let response = await this.getJobs(page, pageSize);
100-
if (response) {
101-
this.jobs = response.data.jobs.entities;
102-
this.pageCount = response.data.jobs.pageInfo.numPages;
103-
this.page = response.data.jobs.pageInfo.page;
103+
this.isLoading = true;
104+
try {
105+
let response = await this.getJobs(page, pageSize);
106+
if (response.data.jobs.entities) {
107+
this.jobs = response.data.jobs.entities;
108+
this.pageCount = response.data.jobs.pageInfo.numPages;
109+
this.page = response.data.jobs.pageInfo.page;
110+
} else if (response.errors) {
111+
this.error = `Error fetching data: ${this.$getErrorMessage(
112+
response.errors
113+
)}`;
114+
}
115+
} catch (error) {
116+
this.error = `Error fetching data: ${this.$getErrorMessage(error)}`;
117+
} finally {
118+
this.isLoading = false;
104119
}
105120
},
106121
getColor(status) {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import LoadingSpinner from "./LoadingSpinner.vue";
2+
3+
export default {
4+
title: "LoadingSpinner",
5+
excludeStories: /.*Data$/,
6+
};
7+
8+
const template = `<loading-spinner :label="label"/>`;
9+
10+
export const Default = () => ({
11+
components: { LoadingSpinner },
12+
template: template,
13+
data: () => ({
14+
label: null,
15+
}),
16+
});
17+
18+
export const Label = () => ({
19+
components: { LoadingSpinner },
20+
template: template,
21+
data: () => ({
22+
label: "Loading...",
23+
}),
24+
});

ui/src/components/LoadingSpinner.vue

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<template>
2+
<div class="d-flex flex-column justify-center align-center pa-4 h-100">
3+
<v-progress-circular :size="size" color="primary" indeterminate />
4+
<p v-if="label" class="subtitle-1 text-medium-emphasis mt-4">{{ label }}</p>
5+
</div>
6+
</template>
7+
<script>
8+
export default {
9+
name: "LoadingSpinner",
10+
props: {
11+
label: {
12+
type: String,
13+
required: false,
14+
default: null,
15+
},
16+
size: {
17+
type: [String, Number],
18+
required: false,
19+
default: 48,
20+
},
21+
},
22+
};
23+
</script>

ui/src/components/OrganizationsTable.stories.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,41 @@ export const Groups = () => ({
301301
name: "Groups",
302302
}),
303303
});
304+
305+
export const OnError = () => ({
306+
components: { OrganizationsTable },
307+
template: OrganizationsTableTemplate,
308+
methods: {
309+
getOrganizations(page, items, filters) {
310+
throw new Error("Test error message");
311+
},
312+
enroll() {
313+
return true;
314+
},
315+
addOrganization() {
316+
return;
317+
},
318+
addDomain() {
319+
return;
320+
},
321+
deleteDomain() {
322+
return;
323+
},
324+
deleteOrganization() {
325+
return;
326+
},
327+
addTeam() {
328+
return;
329+
},
330+
deleteTeam() {
331+
return;
332+
},
333+
fetchTeams() {
334+
return;
335+
},
336+
},
337+
data: () => ({
338+
isGroup: false,
339+
name: "Organizations",
340+
}),
341+
});

ui/src/components/OrganizationsTable.vue

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@
9898
></v-text-field>
9999
</div>
100100
</template>
101+
<template v-slot:no-data>
102+
<v-alert v-if="error" class="text-left" density="compact" type="error">
103+
{{ error }}
104+
</v-alert>
105+
<p v-else-if="Object.keys(filters).length > 0">
106+
No results matched your search.
107+
</p>
108+
<p v-else>No data available</p>
109+
</template>
101110
</v-data-table-server>
102111

103112
<organization-modal
@@ -267,6 +276,7 @@ export default {
267276
teamName: "",
268277
},
269278
loading: false,
279+
error: null,
270280
};
271281
},
272282
created() {
@@ -275,14 +285,20 @@ export default {
275285
methods: {
276286
async getTableItems(page = this.page, filters = this.filters) {
277287
this.loading = true;
278-
let response = await this.fetchPage(page, this.itemsPerPage, filters);
279-
if (response) {
280-
this.items = response.entities;
281-
this.pageCount = response.pageInfo.numPages;
282-
this.page = response.pageInfo.page;
283-
this.totalResults = response.pageInfo.totalResults;
288+
this.error = null;
289+
try {
290+
let response = await this.fetchPage(page, this.itemsPerPage, filters);
291+
if (response) {
292+
this.items = response.entities;
293+
this.pageCount = response.pageInfo.numPages;
294+
this.page = response.pageInfo.page;
295+
this.totalResults = response.pageInfo.totalResults;
296+
}
297+
} catch (error) {
298+
this.error = this.$getErrorMessage(error);
299+
} finally {
300+
this.loading = false;
284301
}
285-
this.loading = false;
286302
},
287303
confirmEnroll(event) {
288304
Object.assign(this.dialog, {

0 commit comments

Comments
 (0)