Skip to content

Commit 3a4b6c5

Browse files
skwowetjoanagmaia
andauthored
fix: improve project group list perf and dropdown filter (#4344)
Co-authored-by: Joana Maia <jmaia@contractor.linuxfoundation.org> Signed-off-by: Yeganathan S <63534555+skwowet@users.noreply.github.com> Signed-off-by: Joana Maia <jmaia@contractor.linuxfoundation.org>
1 parent 3f4a78e commit 3a4b6c5

16 files changed

Lines changed: 160 additions & 115 deletions

File tree

backend/src/database/repositories/segmentRepository.ts

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,12 @@ class SegmentRepository extends RepositoryBase<
463463
if (adminSegments.length === 0) {
464464
return { count: 0, rows: [], limit: criteria.limit, offset: criteria.offset }
465465
}
466-
segmentsSearchQuery += `AND sp.id IN (:adminSegments)`
466+
segmentsSearchQuery += `AND EXISTS (
467+
SELECT 1 FROM segments sp
468+
WHERE sp."grandparentSlug" = f.slug
469+
AND sp."tenantId" = f."tenantId"
470+
AND sp.id IN (:adminSegments)
471+
)`
467472
replacements.adminSegments = adminSegments
468473
}
469474

@@ -474,23 +479,7 @@ class SegmentRepository extends RepositoryBase<
474479
SELECT
475480
f.id AS foundation_id,
476481
f.name AS foundation_name,
477-
f.status,
478-
f."createdAt",
479-
f."updatedAt",
480-
f."sourceId",
481-
f."sourceParentId",
482-
f.slug,
483-
p.name AS project_name,
484-
p.id AS project_id,
485-
p.status AS project_status,
486-
p.slug AS project_slug,
487-
COUNT(DISTINCT sp.id) AS subproject_count,
488-
JSONB_AGG(JSONB_BUILD_OBJECT(
489-
'id', sp.id,
490-
'name', sp.name,
491-
'status', sp.status,
492-
'slug', sp.slug
493-
)) AS subprojects
482+
COUNT(DISTINCT p.id)::int AS project_count
494483
FROM segments f
495484
JOIN segments p
496485
ON p."parentSlug" = f."slug"
@@ -503,22 +492,15 @@ class SegmentRepository extends RepositoryBase<
503492
WHERE f."parentSlug" IS NULL
504493
AND f."tenantId" = :tenantId
505494
${segmentsSearchQuery}
506-
GROUP BY f."id", p.id
495+
GROUP BY f.id
507496
)
508497
SELECT
509498
s.*,
510499
COUNT(*) OVER () AS "totalCount",
511-
JSONB_AGG(JSONB_BUILD_OBJECT(
512-
'id', f.project_id,
513-
'name', f.project_name,
514-
'status', f.project_status,
515-
'slug', f.project_slug,
516-
'subprojects', f.subprojects
517-
)) AS projects
500+
f.project_count AS "projectCount"
518501
FROM segments s
519502
JOIN foundations f ON s.id = f.foundation_id
520503
${searchQuery}
521-
GROUP BY s.id, f.foundation_name
522504
ORDER BY f.foundation_name
523505
${this.getPaginationString(criteria)};
524506
`,

frontend/nginx.kube.conf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ http {
1818

1919
charset utf-8;
2020

21+
gzip on;
22+
gzip_comp_level 5;
23+
gzip_min_length 1024;
24+
gzip_proxied any;
25+
gzip_vary on;
26+
gzip_types
27+
text/plain
28+
text/css
29+
text/javascript
30+
application/javascript
31+
application/json
32+
application/xml
33+
image/svg+xml;
34+
2135
server {
2236
listen 8081 default_server;
2337
listen [::]:8081;

frontend/src/app.vue

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
<script>
2424
import { mapActions } from 'vuex';
2525
import AppResizePage from '@/modules/layout/pages/resize-page.vue';
26-
import { mapActions as piniaMapActions, storeToRefs } from 'pinia';
27-
import { useActivityStore } from '@/modules/activity/store/pinia';
28-
import { useActivityTypeStore } from '@/modules/activity/store/type';
26+
import { storeToRefs } from 'pinia';
2927
import { useAuthStore } from '@/modules/auth/store/auth.store';
3028
import useSessionTracking from '@/shared/modules/monitoring/useSessionTracking';
3129
import { useLfSegmentsStore } from '@/modules/lf/segments/store';
@@ -58,8 +56,10 @@ export default {
5856
tenant: {
5957
handler(tenant, oldTenant) {
6058
if (tenant?.id && tenant.id !== oldTenant?.id) {
61-
this.fetchActivityTypes();
62-
this.fetchActivityChannels();
59+
this.listProjectGroups({
60+
limit: 20,
61+
reset: true,
62+
});
6363
}
6464
},
6565
},
@@ -72,10 +72,6 @@ export default {
7272
if (queryParameters.get('state') === 'noconnect' && window.location.pathname.includes('/integration')) {
7373
return;
7474
}
75-
this.listProjectGroups({
76-
limit: null,
77-
reset: true,
78-
});
7975
if (['/auth/callback'].includes(window.location.pathname)) {
8076
return;
8177
}
@@ -91,12 +87,6 @@ export default {
9187
...mapActions({
9288
resize: 'layout/resize',
9389
}),
94-
...piniaMapActions(useActivityStore, {
95-
fetchActivityChannels: 'fetchActivityChannels',
96-
}),
97-
...piniaMapActions(useActivityTypeStore, {
98-
fetchActivityTypes: 'fetchActivityTypes',
99-
}),
10090
10191
handleResize() {
10292
this.resize({

frontend/src/modules/activity/components/activity-timeline.vue

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ const props = defineProps({
257257
});
258258
259259
const lsSegmentsStore = useLfSegmentsStore();
260-
const { projectGroups, selectedProjectGroup } = storeToRefs(lsSegmentsStore);
260+
const { selectedProjectGroup, selectedProjectGroupSubprojects } = storeToRefs(lsSegmentsStore);
261261
262262
const enabledPlatforms: IdentityConfig[] = Object.values(lfIdentities);
263263
@@ -274,16 +274,8 @@ const selectedSegment = ref(props.selectedSegment || null);
274274
275275
const isMemberEntity = computed(() => props.entityType === 'member');
276276
277-
const subprojects = computed(() => projectGroups.value.list.reduce((acc, projectGroup) => {
278-
projectGroup.projects.forEach((project) => {
279-
project.subprojects.forEach((subproject) => {
280-
acc[subproject.id] = {
281-
id: subproject.id,
282-
name: subproject.name,
283-
};
284-
});
285-
});
286-
277+
const subprojects = computed(() => selectedProjectGroupSubprojects.value.reduce((acc, sp) => {
278+
acc[sp.id] = { id: sp.id, name: sp.name };
287279
return acc;
288280
}, {}));
289281
@@ -292,7 +284,7 @@ const segments = computed(() => {
292284
return (
293285
getSegmentsFromProjectGroup(selectedProjectGroup.value)?.map(
294286
(s) => subprojects.value[s],
295-
) || []
287+
).filter((s) => !!s) || []
296288
);
297289
}
298290
return (

frontend/src/modules/admin/modules/projects/components/view/lf-project-groups-table.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
</div>
2727
</lf-table-cell>
2828
<lf-table-cell>
29-
<app-lf-project-column :projects="projectGroup.projects" />
29+
<app-lf-project-column
30+
:projects="projectGroup.projects || []"
31+
:project-count="projectGroup.projectCount"
32+
/>
3033
</lf-table-cell>
3134
<lf-table-cell class="pr-2 flex justify-end">
3235
<app-lf-project-groups-dropdown

frontend/src/modules/admin/modules/projects/pages/project-groups-list.page.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484

8585
<div class="mb-8 flex flex-wrap gap-2.5">
8686
<div class="bg-gray-200 text-gray-900 text-2xs px-2 h-6 flex items-center w-fit rounded-md">
87-
{{ pluralize('project', projectGroup.projects.length, true) }}
87+
{{ pluralize('project', projectGroup.projectCount ?? projectGroup.projects?.length ?? 0, true) }}
8888
</div>
8989
</div>
9090

frontend/src/modules/lf/layout/components/lf-banners.vue

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,11 @@ import LfButton from '@/ui-kit/button/Button.vue';
158158
const ERROR_BANNER_WORKING_DAYS_DISPLAY = 3;
159159
160160
const lsSegmentsStore = useLfSegmentsStore();
161-
const { selectedProjectGroup } = storeToRefs(lsSegmentsStore);
161+
const { selectedProjectGroup, selectedProjectGroupSubprojects } = storeToRefs(lsSegmentsStore);
162162
const integrations = ref([]);
163163
const fetchIntegrationTimer = ref(null);
164164
const loading = ref(true);
165-
const subProjects = ref([]);
165+
const subProjects = computed(() => selectedProjectGroupSubprojects.value);
166166
167167
const route = useRoute();
168168
@@ -252,21 +252,6 @@ watch(selectedProjectGroup, (updatedProjectGroup, previousProjectGroup) => {
252252
loading.value = true;
253253
fetchIntegrations(updatedProjectGroup);
254254
}
255-
256-
if (!updatedProjectGroup) {
257-
subProjects.value = [];
258-
} else {
259-
subProjects.value = updatedProjectGroup.projects
260-
.reduce((acc, project) => {
261-
project.subprojects.forEach((subproject) => {
262-
if (subproject) {
263-
acc.push(subproject);
264-
}
265-
});
266-
267-
return acc;
268-
}, []);
269-
}
270255
}, {
271256
deep: true,
272257
immediate: true,

frontend/src/modules/lf/layout/components/lf-menu-project-group-selection.vue

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
>
1010
<template #reference>
1111
<el-input
12-
v-model="model"
12+
:model-value="selectedProjectGroup?.name ?? ''"
1313
class="project-groups-select-input"
1414
placeholder="Select project group..."
1515
readonly
@@ -64,7 +64,7 @@
6464
{{ projectGroup.name }}
6565
</div>
6666
<div class="text-tiny text-gray-400">
67-
{{ pluralize("project", projectGroup.projects.length, true) }}
67+
{{ pluralize("project", projectGroup.projectCount ?? projectGroup.projects?.length ?? 0, true) }}
6868
</div>
6969
</div>
7070
</div>
@@ -147,15 +147,6 @@ let scrollContainer: HTMLElement | null = null;
147147
148148
const { trackEvent } = useProductTracking();
149149
150-
const model = computed({
151-
get() {
152-
return selectedProjectGroup.value?.name;
153-
},
154-
set(id) {
155-
updateSelectedProjectGroup(id);
156-
},
157-
});
158-
159150
const queryKey = computed(() => [
160151
TanstackKey.ADMIN_PROJECT_GROUPS,
161152
searchValue.value,
@@ -230,18 +221,18 @@ onMounted(() => {
230221
});
231222
});
232223
233-
const onOptionClick = ({ id, name }: ProjectGroup) => {
224+
const onOptionClick = (projectGroup: ProjectGroup) => {
234225
trackEvent({
235226
key: FeatureEventKey.SELECT_PROJECT_GROUP,
236227
type: EventType.FEATURE,
237228
properties: {
238-
projectGroupId: id,
239-
projectName: name,
229+
projectGroupId: projectGroup.id,
230+
projectName: projectGroup.name,
240231
},
241232
});
242233
243234
isPopoverVisible.value = false;
244-
updateSelectedProjectGroup(id);
235+
updateSelectedProjectGroup(projectGroup);
245236
};
246237
247238
onBeforeUnmount(() => {

frontend/src/modules/lf/segments/lf-segments-service.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ export class LfService {
8989
return response.data;
9090
}
9191

92+
static async querySubprojectsLite(body) {
93+
const response = await authAxios.post(
94+
'/segment/subproject/query-lite',
95+
{
96+
...body,
97+
excludeSegments: true,
98+
},
99+
);
100+
101+
return response.data;
102+
}
103+
92104
// Sub-projects
93105

94106
static async createSubProject(body) {

frontend/src/modules/lf/segments/store/actions.js

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,19 +270,70 @@ export default {
270270

271271
this.listProjects();
272272
},
273-
updateSelectedProjectGroup(projectGroupId, sendToDashboard = true) {
274-
if (projectGroupId) {
275-
const projectGroup = this.projectGroups.list.find((p) => p.id === projectGroupId);
273+
async fetchSubprojectsForProjectGroup(grandparentSlug) {
274+
if (!grandparentSlug) {
275+
this.selectedProjectGroupSubprojects = [];
276+
return;
277+
}
278+
279+
this.selectedProjectGroupSubprojects = [];
280+
281+
try {
282+
const response = await LfService.querySubprojectsLite({
283+
limit: null,
284+
offset: 0,
285+
filter: { grandparentSlug },
286+
});
287+
if (this.selectedProjectGroup?.slug === grandparentSlug) {
288+
this.selectedProjectGroupSubprojects = response.rows || [];
289+
}
290+
} catch (e) {
291+
if (this.selectedProjectGroup?.slug === grandparentSlug) {
292+
this.selectedProjectGroupSubprojects = [];
293+
}
294+
}
295+
},
296+
297+
async updateSelectedProjectGroup(projectGroupOrId, sendToDashboard = true) {
298+
if (!projectGroupOrId) {
299+
this.selectedProjectGroup = null;
300+
this.selectedProjectGroupSubprojects = [];
301+
return;
302+
}
303+
304+
const id = typeof projectGroupOrId === 'string' ? projectGroupOrId : projectGroupOrId.id;
305+
if (!id) {
306+
this.selectedProjectGroup = null;
307+
this.selectedProjectGroupSubprojects = [];
308+
return;
309+
}
310+
311+
try {
312+
let projectGroup = this.projectGroups.list.find((p) => p.id === id) || null;
313+
if (!projectGroup) {
314+
await this.listProjectGroups({ limit: null, reset: true });
315+
projectGroup = this.projectGroups.list.find((p) => p.id === id) || null;
316+
}
317+
318+
if (!projectGroup?.id) {
319+
this.selectedProjectGroup = null;
320+
this.selectedProjectGroupSubprojects = [];
321+
ToastStore.error('Project group could not be loaded');
322+
return;
323+
}
276324

277325
this.selectedProjectGroup = projectGroup;
326+
await this.fetchSubprojectsForProjectGroup(projectGroup.slug);
278327

279328
if (sendToDashboard) {
280329
router.push({
281330
name: 'member',
282331
});
283332
}
284-
} else {
333+
} catch (e) {
285334
this.selectedProjectGroup = null;
335+
this.selectedProjectGroupSubprojects = [];
336+
ToastStore.error('Something went wrong while selecting the project group');
286337
}
287338
},
288339
doChangeProjectGroupCurrentPage(currentPage) {

0 commit comments

Comments
 (0)