Skip to content

Commit 1870bea

Browse files
MRashad26asithade
andauthored
refactor(dashboards): remove My Projects section from project dashboard (#609)
Removes the My Projects vertical slice entirely (Option 2 from LFXV2-1593): Frontend: - Remove @defer block and skeleton placeholder from project-dashboard.component.html - Remove MyProjectsComponent import from project-dashboard.component.ts - Delete apps/.../dashboards/components/my-projects/ directory - Remove getMyProjects() and UserProjectsResponse import from analytics.service.ts Backend: - Remove GET /my-projects route from analytics.route.ts - Remove getMyProjects() controller method from analytics.controller.ts - Remove getMyProjects() Snowflake method from user.service.ts - Remove ProjectItem, UserProjectContributionRow, UserProjectsResponse imports Shared types: - Remove ProjectItem and ProjectItemWithCharts from components.interface.ts - Remove UserProjectContributionRow and UserProjectsResponse from analytics-data.interface.ts Fixes LFXV2-1593. Signed-off-by: Rashad <mrashad@contractor.linuxfoundation.org> Co-authored-by: Asitha de Silva <asithade@gmail.com>
1 parent 061b269 commit 1870bea

11 files changed

Lines changed: 1 addition & 421 deletions

File tree

apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.html

Lines changed: 0 additions & 90 deletions
This file was deleted.

apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.scss

Lines changed: 0 additions & 2 deletions
This file was deleted.

apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.ts

Lines changed: 0 additions & 72 deletions
This file was deleted.

apps/lfx-one/src/app/modules/dashboards/project-dashboard/project-dashboard.component.html

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,5 @@ <h1 class="font-display font-light text-2xl">{{ selectedProject()?.name }} Overv
6363
</div>
6464
</section>
6565
}
66-
67-
<!-- My Projects - Full Width (below fold) -->
68-
@defer (on viewport) {
69-
<lfx-my-projects />
70-
} @placeholder {
71-
<div data-testid="project-dashboard-my-projects-placeholder">
72-
<div class="flex items-center justify-between mb-4">
73-
<div class="flex items-center gap-2">
74-
<p-skeleton width="1.125rem" height="1.125rem" borderRadius="4px" />
75-
<p-skeleton width="6rem" height="1rem" />
76-
</div>
77-
</div>
78-
<p-skeleton width="100%" height="200px" borderRadius="8px" />
79-
</div>
80-
}
8166
</div>
8267
</div>

apps/lfx-one/src/app/modules/dashboards/project-dashboard/project-dashboard.component.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ import { SkeletonModule } from 'primeng/skeleton';
77

88
import { DashboardQuicklinksComponent } from '../components/dashboard-quicklinks/dashboard-quicklinks.component';
99
import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
10-
import { MyProjectsComponent } from '../components/my-projects/my-projects.component';
1110
import { RecentProgressComponent } from '../components/recent-progress/recent-progress.component';
1211

1312
@Component({
1413
selector: 'lfx-project-dashboard',
15-
imports: [RecentProgressComponent, MyMeetingsComponent, MyProjectsComponent, SkeletonModule, DashboardQuicklinksComponent],
14+
imports: [RecentProgressComponent, MyMeetingsComponent, SkeletonModule, DashboardQuicklinksComponent],
1615
templateUrl: './project-dashboard.component.html',
1716
styleUrl: './project-dashboard.component.scss',
1817
})

apps/lfx-one/src/app/shared/services/analytics.service.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import {
4646
UniqueContributorsDailyResponse,
4747
UniqueContributorsWeeklyResponse,
4848
UserCodeCommitsResponse,
49-
UserProjectsResponse,
5049
UserPullRequestsResponse,
5150
EmailCtrResponse,
5251
EngagedCommunitySizeResponse,
@@ -136,21 +135,6 @@ export class AnalyticsService {
136135
);
137136
}
138137

139-
/**
140-
* Get user's projects with activity data
141-
* @returns Observable of user projects response
142-
*/
143-
public getMyProjects(): Observable<UserProjectsResponse> {
144-
return this.http.get<UserProjectsResponse>('/api/analytics/my-projects').pipe(
145-
catchError(() => {
146-
return of({
147-
data: [],
148-
totalProjects: 0,
149-
});
150-
})
151-
);
152-
}
153-
154138
/**
155139
* Get organization maintainers data with monthly trend
156140
* @param accountId - Required account ID to filter by specific organization

apps/lfx-one/src/server/controllers/analytics.controller.ts

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -119,36 +119,6 @@ export class AnalyticsController {
119119
}
120120
}
121121

122-
/**
123-
* GET /api/analytics/my-projects
124-
* Get user's projects with activity data
125-
*/
126-
public async getMyProjects(req: Request, res: Response, next: NextFunction): Promise<void> {
127-
const startTime = logger.startOperation(req, 'get_my_projects');
128-
129-
try {
130-
// Get LF username from OIDC context
131-
const lfUsername = await getUsernameFromAuth(req);
132-
133-
if (!lfUsername) {
134-
throw new AuthenticationError('User username not found in authentication context', {
135-
operation: 'get_my_projects',
136-
});
137-
}
138-
139-
const response = await this.userService.getMyProjects(lfUsername);
140-
141-
logger.success(req, 'get_my_projects', startTime, {
142-
returned_projects: response.data.length,
143-
total_projects: response.totalProjects,
144-
});
145-
146-
res.json(response);
147-
} catch (error) {
148-
next(error);
149-
}
150-
}
151-
152122
/**
153123
* GET /api/analytics/certified-employees
154124
* Get certified employees data for an organization with monthly trend data

apps/lfx-one/src/server/routes/analytics.route.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ const analyticsController = new AnalyticsController();
1313
router.get('/active-weeks-streak', (req, res, next) => analyticsController.getActiveWeeksStreak(req, res, next));
1414
router.get('/pull-requests-merged', (req, res, next) => analyticsController.getPullRequestsMerged(req, res, next));
1515
router.get('/code-commits', (req, res, next) => analyticsController.getCodeCommits(req, res, next));
16-
router.get('/my-projects', (req, res, next) => analyticsController.getMyProjects(req, res, next));
17-
1816
// Certified employees endpoint
1917
router.get('/certified-employees', (req, res, next) => analyticsController.getCertifiedEmployees(req, res, next));
2018

apps/lfx-one/src/server/services/user.service.ts

Lines changed: 0 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,12 @@ import {
2121
PastMeeting,
2222
PastMeetingParticipant,
2323
PendingActionItem,
24-
ProjectItem,
2524
QueryServiceResponse,
2625
UserCodeCommitsResponse,
2726
UserCodeCommitsRow,
2827
UserMetadata,
2928
UserMetadataUpdateRequest,
3029
UserMetadataUpdateResponse,
31-
UserProjectContributionRow,
32-
UserProjectsResponse,
3330
UserPullRequestsResponse,
3431
UserPullRequestsRow,
3532
Vote,
@@ -367,96 +364,6 @@ export class UserService {
367364
};
368365
}
369366

370-
/**
371-
* Get user's projects with activity data
372-
* Queries USER_PROJECT_CONTRIBUTIONS_DAILY table filtered by LF username
373-
* @param lfUsername - Linux Foundation username from OIDC
374-
* @returns All projects with activity data for the user
375-
*/
376-
public async getMyProjects(lfUsername: string): Promise<UserProjectsResponse> {
377-
// Get all projects with their activity data
378-
// Aggregates affiliations per project and sums activities by date
379-
const query = `
380-
WITH UserProjects AS (
381-
SELECT PROJECT_ID, PROJECT_NAME, PROJECT_SLUG, PROJECT_LOGO,
382-
MAX(IS_MAINTAINER) AS IS_MAINTAINER
383-
FROM ANALYTICS.PLATINUM_LFX_ONE.USER_PROJECT_CONTRIBUTIONS_DAILY
384-
WHERE SUB = ?
385-
GROUP BY PROJECT_ID, PROJECT_NAME, PROJECT_SLUG, PROJECT_LOGO
386-
ORDER BY PROJECT_NAME, PROJECT_ID
387-
),
388-
ProjectAffiliations AS (
389-
SELECT PROJECT_ID, LISTAGG(DISTINCT AFFILIATION, ', ') WITHIN GROUP (ORDER BY AFFILIATION) AS AFFILIATIONS
390-
FROM ANALYTICS.PLATINUM_LFX_ONE.USER_PROJECT_CONTRIBUTIONS_DAILY
391-
WHERE SUB = ?
392-
AND AFFILIATION IS NOT NULL
393-
AND AFFILIATION != ''
394-
GROUP BY PROJECT_ID
395-
),
396-
DailyActivities AS (
397-
SELECT PROJECT_ID, ACTIVITY_DATE,
398-
SUM(DAILY_CODE_ACTIVITIES) AS DAILY_CODE_ACTIVITIES,
399-
SUM(DAILY_NON_CODE_ACTIVITIES) AS DAILY_NON_CODE_ACTIVITIES
400-
FROM ANALYTICS.PLATINUM_LFX_ONE.USER_PROJECT_CONTRIBUTIONS_DAILY
401-
WHERE SUB = ?
402-
GROUP BY PROJECT_ID, ACTIVITY_DATE
403-
)
404-
SELECT
405-
p.PROJECT_ID,
406-
p.PROJECT_NAME,
407-
p.PROJECT_SLUG,
408-
p.PROJECT_LOGO,
409-
p.IS_MAINTAINER,
410-
COALESCE(pa.AFFILIATIONS, '') AS AFFILIATION,
411-
a.ACTIVITY_DATE,
412-
a.DAILY_CODE_ACTIVITIES,
413-
a.DAILY_NON_CODE_ACTIVITIES
414-
FROM UserProjects p
415-
LEFT JOIN ProjectAffiliations pa ON p.PROJECT_ID = pa.PROJECT_ID
416-
LEFT JOIN DailyActivities a ON p.PROJECT_ID = a.PROJECT_ID
417-
ORDER BY p.PROJECT_NAME, p.PROJECT_ID, a.ACTIVITY_DATE ASC
418-
`;
419-
420-
const result = await this.snowflakeService.execute<UserProjectContributionRow>(query, [lfUsername, lfUsername, lfUsername]);
421-
422-
// Group rows by PROJECT_ID and transform into ProjectItem[]
423-
const projectsMap = new Map<string, ProjectItem>();
424-
425-
for (const row of result.rows) {
426-
if (!projectsMap.has(row.PROJECT_ID)) {
427-
// Parse affiliations from comma-separated string
428-
const affiliations = row.AFFILIATION ? row.AFFILIATION.split(', ').filter((a) => a.trim()) : [];
429-
430-
// Initialize new project
431-
projectsMap.set(row.PROJECT_ID, {
432-
name: row.PROJECT_NAME,
433-
slug: row.PROJECT_SLUG,
434-
logo: row.PROJECT_LOGO || undefined,
435-
role: row.IS_MAINTAINER ? 'Maintainer' : 'Contributor',
436-
affiliations,
437-
codeActivities: [],
438-
nonCodeActivities: [],
439-
});
440-
}
441-
442-
// Add daily activity values to arrays (if there's activity data)
443-
if (row.ACTIVITY_DATE) {
444-
const project = projectsMap.get(row.PROJECT_ID)!;
445-
project.codeActivities.push(row.DAILY_CODE_ACTIVITIES || 0);
446-
project.nonCodeActivities.push(row.DAILY_NON_CODE_ACTIVITIES || 0);
447-
}
448-
}
449-
450-
// Convert map to array; ROOT is an administrative pseudo-project used only for persona
451-
// detection and must never surface in user-facing project lists.
452-
const projects = Array.from(projectsMap.values()).filter((p) => p.slug !== ROOT_PROJECT_SLUG);
453-
454-
return {
455-
data: projects,
456-
totalProjects: projects.length,
457-
};
458-
}
459-
460367
/**
461368
* Get all pending actions for the authenticated user across every persona. A pending action
462369
* is a pending action regardless of which dashboard the user is viewing — the board-only

0 commit comments

Comments
 (0)