Skip to content

feat(mentorship): add mentorship module with routing, components, and services - #2223

Open
Sameh16 wants to merge 9 commits into
mainfrom
feat/mentorship-admin-ui
Open

feat(mentorship): add mentorship module with routing, components, and services#2223
Sameh16 wants to merge 9 commits into
mainfrom
feat/mentorship-admin-ui

Conversation

@Sameh16

@Sameh16 Sameh16 commented Sep 7, 2026

Copy link
Copy Markdown

Overview

feat(mentorship): add mentorship module with routing, components, and services

  • Introduced a new mentorship module with routing for the mentorship admin interface.
  • Added MentorshipService to handle API interactions for mentorship programs.
  • Created AdminComponent for the mentorship admin page, including its HTML template.
  • Implemented mentorship routes and integrated them into the main application routing.
  • Updated sidebar navigation to include mentorship section and admin link.
  • Added constants and interfaces for mentorship-related data structures.

This commit lays the groundwork for the mentorship feature, enabling program management and administration.

Summary

This pull request introduces the initial implementation of the Mentorship module in both the frontend and backend, including routing, navigation, service integration, and API endpoints. The changes establish the foundation for mentorship program administration, including listing, enrolling, and validating mentorship programs, and integrate these features into the main app navigation and API.

Mentorship Module Integration

  • Added the Mentorship section to the sidebar navigation, with an "Admin" link pointing to /mentorship/admin (sidebar-nav.service.ts).
  • Updated application routes to include the new /mentorship path and lazy-loaded mentorship routes in app.routes.ts.
  • Created mentorship module routes, redirecting the base path to /admin and loading the AdminComponent.
  • Added the mentorship admin page (AdminComponent and template) as the initial frontend entry point. [1] [2]

Frontend Service Layer

  • Implemented MentorshipService in the frontend, providing methods for listing programs, enrolling, checking name availability, fetching LF projects, and retrieving CII badge data. Error handling ensures UI resilience on failures.

Backend API Implementation

  • Added a new Express router (mentorship.route.ts) and controller (mentorship.controller.ts) with endpoints for mentorship program CRUD operations, LF project lookup, and CII badge retrieval. Includes input validation and authentication checks. [1] [2]
  • Registered the mentorship API routes under /api/mentorship in the main server configuration. [1] [2]

… services

- Introduced a new mentorship module with routing for the mentorship admin interface.
- Added `MentorshipService` to handle API interactions for mentorship programs.
- Created `AdminComponent` for the mentorship admin page, including its HTML template.
- Implemented mentorship routes and integrated them into the main application routing.
- Updated sidebar navigation to include mentorship section and admin link.
- Added constants and interfaces for mentorship-related data structures.

This commit lays the groundwork for the mentorship feature, enabling program management and administration.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>
@Sameh16
Sameh16 requested review from lewisojile and mlehotskylf and a balanced review from Copilot September 7, 2026 14:58
@Sameh16 Sameh16 self-assigned this Sep 7, 2026
@Sameh16
Sameh16 requested a review from a team as a code owner September 7, 2026 14:58
@Sameh16 Sameh16 added deploy-preview do-not-merge Indicates that the pull request should NOT be merged. labels Sep 7, 2026
@cursor

cursor Bot commented Sep 7, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
New authenticated write path (program enrollment) and outbound CII fetch on the BFF; persistence is process-local mocks, so behavior will change when a real backend is wired.

Overview
Adds a Mentorship area under the Me lens: sidebar link, /mentorship lazy routes (admin list and /admin/enroll wizard), and shared types/constants/validation utilities (with unit tests).

The admin page loads programs from the BFF with debounced search, status filter, and offset pagination; cards show status and mentor/mentee metrics. Enroll is a three-step wizard (details, setup, prerequisites) with import-from-program, LF project lazy select, CII/name async checks, terms dialog, custom prerequisites, and submit via MentorshipService.enrollProgram. Program card navigation is still a no-op pending detail work.

The BFF mounts /api/mentorship/* (list, detail, enroll, name check, LF projects, CII badge) with auth and server-side enroll validation; data is in-memory mocks until a real mentorship service exists. CII lookup calls bestpractices.dev with hardened URL building.

Shared UI gains optional inputId, select onFilter/onLazyLoad, and input readonly/maxlength to support the wizard. Tailwind safelists mentorship badge/avatar classes.

Reviewed by Cursor Bugbot for commit 13dd31c. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Essentials

Run ID: f0fb7c9a-7eb1-44e6-bd44-7a724b6fcd66

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

Comment thread apps/lfx-one/src/server/services/mentorship.service.ts Fixed

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread apps/lfx-one/src/server/controllers/mentorship.controller.ts
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

🚀 Deployment Status

Your branch has been deployed to: https://ui-pr-2223.dev.v2.cluster.linuxfound.info

Deployment Details:

  • Environment: Development
  • Namespace: ui-pr-2223
  • ArgoCD App: ui-pr-2223

The deployment will be automatically removed when this PR is closed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces the initial mentorship administration surface across shared contracts, the Angular application, and Express BFF.

Changes:

  • Adds mentorship models, validation utilities, constants, and tests.
  • Adds mock-backed mentorship API endpoints and frontend service methods.
  • Adds lazy routing, sidebar navigation, and an admin landing page.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/shared/src/utils/mentorship.utils.ts Adds mentorship validation and formatting helpers.
packages/shared/src/utils/mentorship.utils.spec.ts Tests shared mentorship utilities.
packages/shared/src/utils/index.ts Exports mentorship utilities.
packages/shared/src/interfaces/mentorship.interface.ts Defines mentorship API and form contracts.
packages/shared/src/interfaces/index.ts Exports mentorship interfaces.
packages/shared/src/constants/mentorship.constants.ts Adds statuses, styles, and mock programs.
packages/shared/src/constants/mentorship-program-detail.constants.ts Adds mock program-detail records.
packages/shared/src/constants/mentorship-enroll.constants.ts Adds enrollment configuration and mock data.
packages/shared/src/constants/index.ts Exports mentorship constants.
apps/lfx-one/src/server/services/mentorship.service.ts Implements mock-backed mentorship operations.
apps/lfx-one/src/server/server.ts Mounts the mentorship API router.
apps/lfx-one/src/server/routes/mentorship.route.ts Defines mentorship API routes.
apps/lfx-one/src/server/controllers/mentorship.controller.ts Handles authentication, parsing, and validation.
apps/lfx-one/src/app/shared/services/sidebar-nav.service.ts Adds Mentorship navigation.
apps/lfx-one/src/app/shared/services/mentorship.service.ts Adds the Angular API client.
apps/lfx-one/src/app/modules/mentorship/mentorship.routes.ts Defines mentorship frontend routes.
apps/lfx-one/src/app/modules/mentorship/admin/admin.component.ts Adds the admin landing component.
apps/lfx-one/src/app/modules/mentorship/admin/admin.component.html Adds the admin page template.
apps/lfx-one/src/app/app.routes.ts Registers the mentorship module.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/lfx-one/src/server/routes/mentorship.route.ts
Comment thread apps/lfx-one/src/server/services/mentorship.service.ts
Comment thread apps/lfx-one/src/server/services/mentorship.service.ts Outdated
Comment thread apps/lfx-one/src/server/services/mentorship.service.ts
Comment thread apps/lfx-one/src/server/services/mentorship.service.ts Outdated
Comment thread packages/shared/src/utils/mentorship.utils.ts
- Updated `getPrograms` method in `MentorshipService` to accept `offset` and `limit` parameters for pagination.
- Introduced `parseIntQuery` utility in `MentorshipController` to safely parse query parameters for pagination.
- Modified controller methods to utilize the new pagination parameters when fetching mentorship programs and projects.
- Added validation for ISO date formats in mentorship term handling to ensure accurate date processing.

This commit improves the mentorship program retrieval functionality by allowing clients to paginate results, enhancing performance and usability.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>
Copilot AI review requested due to automatic review settings September 7, 2026 15:24
Comment thread apps/lfx-one/src/server/services/mentorship.service.ts Fixed

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread apps/lfx-one/src/server/services/mentorship.service.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

apps/lfx-one/src/server/services/mentorship.service.ts:132

  • The availability check does not use the same uniqueness rule as enrollment. For example, with the existing GridFlow: Time-Series Ingestion Pipeline, querying GridFlow Time Series Ingestion Pipeline returns available: true, but enrollment rejects it because both names generate the same slug. Include the slug collision check here so the preflight result agrees with POST.
    packages/shared/src/constants/mentorship-enroll.constants.ts:329
  • This default embeds a one-time 2026/2027 schedule, but every new form clones it. After 2026-12-01, getMentorshipTermDateErrors rejects its application start as being in the past, so a newly opened enrollment wizard starts with an already-invalid term and becomes increasingly stale. Generate a future default schedule when creating the form instead of exporting fixed calendar dates.
    apps/lfx-one/src/server/services/mentorship.service.ts:65
  • The controller already owns the HTTP operation lifecycle, so this starts a second lifecycle for the same request; the same duplication appears in the other methods in this service. .claude/rules/logging-patterns.md:47-75 requires one startOperation per endpoint and reserves service logging for debug/info. Remove the service-level start/success pairs (or replace them with step-level logging only where useful).

Comment thread apps/lfx-one/src/server/services/mentorship.service.ts Outdated
Comment thread packages/shared/src/constants/mentorship-enroll.constants.ts Outdated
Comment thread packages/shared/src/utils/mentorship.utils.ts Outdated
…alidation logging

- Removed the constant `MENTORSHIP_CII_BADGE_JSON_BASE` and replaced it with a direct string concatenation for constructing the CII badge URL in `buildCiiBadgeJsonUrl`.
- Enhanced the validation logic to ensure the project ID is numeric and greater than zero.
- Updated error handling to log the full URL path instead of just the pathname in various error scenarios.
- Improved debug logging in `isValidUrl` to provide clearer messages when rejecting dangerous URL patterns, invalid protocols, and hostnames.

These changes streamline the badge URL construction and enhance the robustness of URL validation, improving overall error handling and logging clarity.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>
Copilot AI review requested due to automatic review settings September 7, 2026 15:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.

Suppressed comments (7)

Previously missed (5) — in code that hasn't changed since the last review.

apps/lfx-one/src/app/shared/services/mentorship.service.ts:58

  • An HTTP failure is not evidence that a name is available. Returning available: true on 401/5xx/network errors gives the wizard a false validation success and lets users continue only to fail at submission. Let the error propagate so the caller can show an unavailable/unknown validation state.
    apps/lfx-one/src/server/services/mentorship.service.ts:106
  • A direct POST can supply any nonblank projectId; this fallback then records that arbitrary identifier as the program's project name. Validate that the ID exists in the LF-project catalog instead of accepting an unknown project, so the API cannot create programs associated with nonexistent projects.
    apps/lfx-one/src/app/modules/mentorship/admin/admin.component.ts:14
  • This documentation describes signal state, toSignal, filters, and a child list component, but the class is currently empty and the template only renders a heading. Update the comment to describe the actual placeholder component so future work is not based on nonexistent behavior.
    apps/lfx-one/src/server/routes/mentorship.route.ts:14
  • The PR description says this router provides mentorship-program CRUD, but it exposes only list/detail reads and create; there is no update or delete route. Either add the promised operations (with matching controller/service handling) or narrow the PR description to the create/read scaffolding actually implemented.
    packages/shared/src/utils/mentorship.utils.spec.ts:270
  • This wall-clock assertion is vulnerable to unrelated CI scheduling pauses and can fail even when the slug algorithm is linear. Keep the long-input output assertion as the regression check and leave complexity detection to CodeQL/static analysis rather than imposing a 100 ms timing threshold.

packages/shared/src/utils/mentorship.utils.ts:188

  • This validates custom due dates only by lexical comparison, so malformed values such as 9999-z sort after today and pass the server-side enrollment validation even though the interface requires YYYY-MM-DD. Reuse the exact calendar-date validator before comparing dates.
    if (isBlank(item.dueDate ?? '')) return true;
    if ((item.dueDate ?? '') <= todayIso) return true;

apps/lfx-one/src/server/services/mentorship.service.ts:130

  • Availability checks only compare normalized names, but enrollment rejects both duplicate names and duplicate slugs. For example, after Foo Bar exists, checking Foo-Bar reports available and submission then returns 409. Apply the same slug collision check in both paths so this endpoint remains truthful.
    const needle = name.trim().toLowerCase();
    const taken = programsStore.some((program) => program.name.trim().toLowerCase() === needle);

Comment thread apps/lfx-one/src/server/services/mentorship.service.ts Outdated
Comment thread apps/lfx-one/src/server/services/mentorship.service.ts
- Introduced a constant for CII badge timeout to manage fetch requests.
- Updated the CII badge URL construction to align with the new host and ensure consistency with the `MENTORSHIP_CII_HOST`.
- Improved validation logic for custom prerequisites in mentorship forms to reject invalid due dates.
- Added a test case to validate the handling of non-date due dates in custom prerequisites.

These changes improve the robustness of the mentorship service by ensuring accurate URL handling and enhancing form validation.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>
Copilot AI review requested due to automatic review settings September 7, 2026 15:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Suppressed comments (11)

Previously missed (6) — in code that hasn't changed since the last review.

apps/lfx-one/src/app/shared/services/mentorship.service.ts:56

  • A failed availability request is reported as available: true. That explicitly approves an unverified name during a BFF/network outage, so the user proceeds only to encounter a submission failure. Propagate the error or represent an indeterminate lookup state rather than treating failure as availability.

This issue also appears on line 74 of the same file.
apps/lfx-one/src/server/services/mentorship.service.ts:107

  • The server only checks that projectId is nonblank, then this fallback accepts any arbitrary value and records it as the sponsoring project name. A direct POST such as projectId: "not-an-lf-project" therefore creates a program tied to a nonexistent project. Resolve the ID against the canonical project list and reject unknown IDs instead of persisting the input.
    packages/shared/src/constants/mentorship-enroll.constants.ts:328
  • These fixed dates make the empty enrollment form expire: beginning 2026-12-02 its application start is already in the past, and beginning 2027-04-01 its term start also fails validation. Derive a future default term from the current date (or start with no term) so opening a new form remains valid over time.
    apps/lfx-one/src/app/modules/mentorship/admin/admin.component.ts:14
  • This documentation describes signals, toSignal, filtering, and a child list component, but the class is empty and the template only renders an Admin heading. Remove the future-design claims or implement the described behavior so maintainers are not sent looking for code that does not exist.
    apps/lfx-one/src/server/controllers/mentorship.controller.ts:80
  • This new controller has no sibling spec even though it defines authentication checks, request-body coercion, query parsing, and all-step validation before writes. Add mentorship.controller.spec.ts covering unauthenticated requests, malformed query/body input, successful enrollment, and forwarding service errors to next.
    apps/lfx-one/src/server/routes/mentorship.route.ts:16
  • The PR description says this router adds mentorship CRUD endpoints, but it exposes only list/detail reads and create; there are no update or delete routes. Either narrow the description to the read/create scaffolding actually shipped or implement the missing operations if CRUD is required for this PR.

apps/lfx-one/src/server/services/mentorship.service.ts:131

  • Availability checks only the normalized name, while enrollProgram also rejects slug collisions. For example, an existing A-B makes A B report available here but fail with 409 on submission because both slugify to a-b. Apply the same name-or-slug predicate in both paths.
    const needle = name.trim().toLowerCase();
    const taken = programsStore.some((program) => program.name.trim().toLowerCase() === needle);

apps/lfx-one/src/app/shared/services/mentorship.service.ts:74

  • This catch collapses every response—including upstream 429/5xx failures—into the same null used for a missing CII project. That discards the backend's new 404-vs-upstream-failure distinction and will make a service outage look like an invalid project ID. Return null only for 404 and propagate other errors so the UI can show a retryable failure.
      catchError(() => of(null))

packages/shared/src/utils/mentorship.utils.ts:248

  • The unanchored shape check and direct Date construction normalize malformed values instead of rejecting them: 2026-02-31 becomes March 3 and trailing junk is accepted. formatMentorshipShortMonthYear can therefore display a plausible but fabricated month for bad API data. Reuse the exact calendar-date validator before constructing the local date.
export function parseMentorshipDateOnly(value: string): Date | null {
  const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(value.trim());
  if (!match) return null;
  return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));

apps/lfx-one/src/server/services/mentorship.service.ts:59

  • This new backend service has no sibling spec despite branching over filtering/pagination, duplicate conflicts, project validation, and external CII response/error mapping. These are contract-bearing paths where regressions would change API responses; add mentorship.service.spec.ts covering the happy paths and representative 404, non-2xx, invalid-input, and conflict cases.
export class MentorshipService {

packages/shared/src/utils/mentorship.utils.ts:223

  • The ISO branch is unanchored and never validates the month/day, so values such as 2026-99-01 or 2026-09-01junk are accepted despite the function's documented contract. Downstream lastDayOfMentorshipMonth then normalizes invalid months into a different year. Require an exact, real YYYY-MM-DD before returning its month/year.
  const iso = /^(\d{4})-(\d{2})(?:-\d{2})?/.exec(trimmed);
  if (iso) {
    return { year: iso[1], month: iso[2] };
  }

Comment thread packages/shared/src/utils/mentorship.utils.ts
- Updated the `isMentorshipCiiProjectId` function to ensure it only accepts positive integers, rejecting '0' and leading zeros.
- Enhanced the corresponding test case to reflect these changes, improving validation accuracy for mentorship project IDs.

These modifications strengthen the input validation for CII project IDs, ensuring compliance with expected numeric formats.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>
Copilot AI review requested due to automatic review settings September 7, 2026 16:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.

Suppressed comments (5)

Previously missed (5) — in code that hasn't changed since the last review.

apps/lfx-one/src/app/shared/services/mentorship.service.ts:58

  • This converts every lookup failure—including authentication, network, and server errors—into available: true. The UI can consequently show a known name as available and only fail later with a 409 on submission. Preserve the request error so the caller can represent availability as unknown rather than affirmatively available.
    apps/lfx-one/src/server/services/mentorship.service.ts:107
  • projectId is only checked for non-blank text, and this fallback turns any caller-supplied value into the persisted project name. A direct POST can therefore create a program for a nonexistent LF project. Resolve the ID against MOCK_MENTORSHIP_LF_PROJECTS and reject unknown IDs with ServiceValidationError instead of storing the raw value.
    packages/shared/src/constants/mentorship-enroll.constants.ts:328
  • The live empty-form factory always inserts this fixed 2027 term. From December 2, 2026 onward its application start is already in the past, so the newly created form immediately fails getMentorshipTermDateErrors; after March 2027 its term start is stale as well. Generate a default schedule relative to the current date instead of using a fixed fixture date.
    packages/shared/src/utils/mentorship.utils.ts:248
  • This parser accepts trailing text and impossible dates, then lets JavaScript normalize them (for example, 2026-02-31 becomes March 3). formatMentorshipShortMonthYear will therefore display a plausible month for malformed API data instead of preserving or rejecting it. Reuse the exact calendar-date validator before constructing the Date.
    apps/lfx-one/src/app/modules/mentorship/admin/admin.component.ts:14
  • This documentation describes signals, toSignal, search/filter state, and a child list that the component does not contain—the class is empty and the template only renders a heading. Remove the copied implementation details so future maintainers are not directed to nonexistent behavior.

… loading states (#2226)

* feat(mentorship): enhance admin interface with program enrollment and loading states

- Added an "Enroll a Program" button to the admin component for program enrollment.
- Implemented loading state handling in the admin component to display a loading indicator when fetching programs.
- Created a new `ProgramsListComponent` to manage the display of mentorship programs, including search and status filtering.
- Introduced `ProgramCardComponent` for individual program representation, showcasing key metrics and details.
- Updated the admin component to utilize the new components for better modularity and maintainability.

These enhancements improve the user experience in the mentorship admin interface by providing clear actions and feedback during data loading.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

* feat(mentorship): implement pagination and loading states in admin programs list

- Enhanced the admin component to support pagination for mentorship programs, allowing users to load more programs dynamically.
- Updated the `ProgramsListComponent` to include loading states and a "Load more" button, improving user experience during data fetching.
- Refactored the admin component to utilize new signals for managing loading states and program offsets.
- Introduced constants for pagination size to standardize program retrieval.

These changes enhance the functionality and usability of the mentorship admin interface by providing a more responsive and interactive experience for managing programs.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

* feat(mentorship): improve admin component with loading states and UI enhancements

- Added a loading state signal to the admin component to manage filter loading during program retrieval.
- Updated the logic for determining if more programs can be loaded based on the new loading state.
- Enhanced the `ProgramsListComponent` and `ProgramCardComponent` for better layout and responsiveness, including adjustments to the HTML structure for improved accessibility and user experience.
- Refactored the program retrieval logic to handle loading states more effectively, ensuring a smoother user interaction when fetching mentorship programs.

These changes enhance the overall usability and responsiveness of the mentorship admin interface, providing clearer feedback during data operations.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

* fix(mentorship): enhance filter handling in admin component

- Added `share()` operator to the filters observable to enable multicast behavior, allowing late subscribers to receive the latest filter values without triggering unnecessary fetches.
- Updated the loading state management to ensure `filterLoading` is cleared after the latest first-page emission, preventing premature loading state resets during in-flight requests.

These changes improve the responsiveness and reliability of the mentorship admin interface during program filtering operations.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

* feat(mentorship): enhance filter application in admin component

- Introduced `appliedSearch` and `appliedStatus` signals to track the last applied filter values post-debounce.
- Added `filtersDirty` computed property to determine if the current filters differ from the applied ones, improving filter handling logic.
- Updated the `hasMore` computed property to account for the new filter state, ensuring accurate loading behavior during program retrieval.

These changes improve the responsiveness and accuracy of the mentorship admin interface when applying filters.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

---------

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>
Copilot AI review requested due to automatic review settings September 7, 2026 18:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

apps/lfx-one/src/server/controllers/mentorship.controller.ts:24

  • Number.parseInt accepts malformed prefixes such as limit=10oops, limit=1.5, and limit=1e3; zero and negative values are then silently clamped in the service. Reject any present value that is not an exact integer (offset ≥ 0, limit > 0) with ServiceValidationError, as the existing user.controller.ts:77-91 pagination parsing does.
    apps/lfx-one/src/server/routes/mentorship.route.ts:14
  • The PR description says this router implements mentorship program CRUD, but the route surface only supports list/get/create; there is no update or delete operation. Either add the missing operations or narrow the description to the read/create scaffolding actually provided.

apps/lfx-one/src/app/modules/mentorship/admin/admin.component.ts:77

  • The enabled primary “Enroll a Program” button calls this no-op, so the main CTA provides no result or feedback. Wire the enrollment route before exposing the action, or render it disabled/coming-soon for this scaffolding phase.
  protected onEnrollProgram(): void {
    // TODO: wire up program-enrollment flow (LFXV2-<TBD>) — currently a no-op
    // so the CTA visibly renders without silently promising navigation the
    // rest of the module doesn't yet support.
    // void this.router.navigate(['/mentorship/admin/enroll']);

Comment thread apps/lfx-one/src/app/modules/mentorship/admin/admin.component.ts Outdated
Comment thread apps/lfx-one/src/app/shared/services/mentorship.service.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/shared/src/constants/mentorship.constants.ts Outdated
- Modified the `onProgramClick` method in the admin component to remove the program ID parameter, simplifying the function call.
- Adjusted the corresponding HTML template to reflect this change, ensuring the program click event is handled correctly without passing an argument.

These updates streamline the event handling in the mentorship admin interface, improving code clarity and maintainability.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>
Copilot AI review requested due to automatic review settings September 7, 2026 18:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.

Suppressed comments (4)

Previously missed (3) — in code that hasn't changed since the last review.

apps/lfx-one/src/app/modules/mentorship/admin/admin.component.ts:77

  • The enabled primary “Enroll a Program” button invokes this no-op, so the page advertises an action that cannot be completed. Add the enrollment route and navigation, or hide/disable the CTA with an unavailable-state explanation until that flow is implemented.
    packages/shared/src/constants/mentorship-enroll.constants.ts:328
  • This default becomes invalid as time advances: after 2026-12-01 its application start is rejected as being before today, and after March 2027 its term start is rejected as being before the current month. Because every empty/imported form copies this constant, new enrollment forms will eventually fail validation without user changes. Generate defaults relative to the current date when creating the form, or leave the date fields empty for the user to select.
    apps/lfx-one/src/server/services/mentorship.service.ts:64
  • This service starts and completes a second HTTP-style logging lifecycle even though the controller already owns that lifecycle; the same duplication occurs in the other methods in this class. That produces two operation records and conflicting duration semantics per request. Per .claude/rules/logging-patterns.md:47-75, keep startOperation/success in controllers and use debug/info for service-level events.

apps/lfx-one/src/app/modules/mentorship/admin/admin.component.ts:62

  • Each program card is rendered as a keyboard-focusable button with a chevron, but its emitted identifier ends here in a no-op. Users can activate the card and receive no navigation or feedback. Wire the detail route/handler before exposing the cards as interactive, or render them as non-interactive content until that flow exists.
  protected onProgramClick(): void {
    // TODO: wire up program-detail flow (LFXV2-<TBD>) — currently a no-op
    // so the CTA visibly renders without silently promising navigation the
    // rest of the module doesn't yet support.
    // void this.router.navigate(['/mentorship/admin', programId]);

- Updated the logic for generating initials in the `ProgramCardComponent` to return only the first letter of the program name, improving clarity and efficiency.
- Adjusted the corresponding documentation to reflect the new behavior.

This change enhances the readability of the code and ensures that the initials are generated consistently based on the program name.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>
Copilot AI review requested due to automatic review settings September 7, 2026 18:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.

Suppressed comments (6)

Previously missed (4) — in code that hasn't changed since the last review.

apps/lfx-one/src/server/services/mentorship.service.ts:107

  • projectId is only checked for non-blankness, and this fallback turns any attacker-supplied value into the persisted/displayed project name. A direct POST can therefore create a program for a nonexistent LF project even though the API exposes a fixed project picker. Resolve the ID against the project catalog and reject unknown IDs before constructing the program.
    packages/shared/src/constants/mentorship-enroll.constants.ts:328
  • This hard-coded application start expires almost immediately: after 2026-12-01, every new form created by createEmptyMentorshipEnrollForm starts with a date that the validator rejects as being in the past. Generate a rolling future term from the current date (or initialize the date fields empty) instead of baking a 2026/2027 schedule into the reusable form factory.
    packages/shared/src/utils/mentorship.utils.ts:223
  • The ISO branch is neither anchored nor calendar-valid, so values such as 2026-13-junk are accepted and lastDayOfMentorshipMonth silently normalizes them into a different year. This contradicts the helper's documented YYYY-MM-DD contract and can make malformed term data look valid. Reuse the strict date predicate before extracting the month.

This issue also appears on line 245 of the same file.
packages/shared/src/utils/mentorship.utils.spec.ts:150

  • This fixed start date makes the test depend on the real clock. Beginning in April 2027, setup validation reports the past startDate before the malformed application-date error, so the assertion below fails. Pin the system date with Vitest fake timers (and restore it afterward) so the test remains deterministic.

apps/lfx-one/src/server/services/mentorship.service.ts:131

  • This availability check does not apply the slug uniqueness rule enforced by enrollProgram. For example, a punctuation variant whose name differs but slug matches an existing program is reported as available and then deterministically fails with 409 on submission. Check both normalized name and generated slug here so the endpoint accurately predicts create eligibility.
    const needle = name.trim().toLowerCase();
    const taken = programsStore.some((program) => program.name.trim().toLowerCase() === needle);

packages/shared/src/utils/mentorship.utils.ts:249

  • This parser accepts trailing text and impossible dates, then lets Date normalize them; for example, 2026-02-31 becomes March and formatMentorshipShortMonthYear displays a plausible but incorrect month. Validate the exact calendar date before constructing it, as formatIsoDateLabel already does.
export function parseMentorshipDateOnly(value: string): Date | null {
  const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(value.trim());
  if (!match) return null;
  return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
}

#2228)

* feat(mentorship): implement program enrollment flow in admin interface

- Added a new route for program enrollment under the admin section, allowing navigation to the enrollment component.
- Created the `EnrollProgramComponent` with a multi-step form for program enrollment, including details, setup, and prerequisites.
- Introduced custom prerequisite handling with the `EnrollCustomPrerequisiteComponent`, enabling users to add specific requirements for their programs.
- Enhanced the `EnrollDetailsStepComponent` and `EnrollPrerequisitesStepComponent` to manage program details and prerequisites effectively.
- Updated the admin component to navigate to the enrollment flow, improving the user experience for program management.

These changes enhance the mentorship admin interface by providing a structured enrollment process for programs, improving usability and functionality.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

* feat(mentorship): enhance enrollment process with improved validation and UI updates

- Updated the `EnrollProgramComponent` to handle cancellation via the `onCancel` method, improving user navigation.
- Refined validation logic in `EnrollDetailsStepComponent` to ensure accurate feedback for program name and CII ID inputs.
- Enhanced the `EnrollCustomPrerequisiteComponent` to utilize observables for item updates, improving reactivity.
- Added loading states and pagination support in the `EnrollDetailsStepComponent` for better user experience during program retrieval.
- Introduced new constants and utility functions for program importability checks, streamlining the enrollment process.

These changes improve the overall functionality and user experience of the mentorship enrollment interface, ensuring a smoother and more responsive interaction for users.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

* fix(mentorship): improve input handling and disable buttons during submission

- Added a `disabled` state to buttons in the `EnrollProgramComponent` to prevent user actions while submitting.
- Updated input elements across various components to use `inputId` instead of `id` for better accessibility and to avoid duplicate IDs.
- Refactored the `onCancel` method to ensure it does not proceed if a submission is in progress.
- Enhanced the `InputTextComponent` and `TextareaComponent` to support the new `inputId` property for improved label association.

These changes enhance the user experience by preventing unintended actions during form submissions and improving accessibility compliance.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

* fix(mentorship): enhance term name input with maxlength attribute

- Added a `maxlength` attribute to the term name input field in the `EnrollTermDialogComponent` to enforce character limits and improve user experience.
- This change ensures that users receive immediate feedback on the maximum allowed length for the term name, enhancing form validation.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

* fix(mentorship): enhance custom prerequisite component with accessibility improvements

- Added a `maxlength` attribute to the prerequisite name input field to enforce character limits.
- Updated the checkbox element to include an `inputId` for better accessibility and associated it with a label for improved user experience.

These changes enhance form validation and accessibility in the custom prerequisite component, ensuring a more user-friendly interface.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

* fix(mentorship): improve error handling and terms acceptance in enrollment process

- Refactored error handling in the `EnrollProgramComponent` to utilize `hasStepErrors()` for better clarity in error display.
- Enhanced the terms acceptance logic by introducing `isMentorshipTermsAccepted()` to handle various checkbox states, ensuring accurate validation.
- Updated the `EnrollPrerequisitesStepComponent` to use PrimeNG's checkbox for terms acceptance, improving accessibility and user experience.
- Adjusted the checkbox component to support dynamic `inputId` for better label association.

These changes enhance the overall validation and user interaction during the mentorship enrollment process, ensuring a smoother experience for users.

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>

---------

Signed-off-by: Sameh16 <sameh_mohamed16@hotmail.com>
Copilot AI review requested due to automatic review settings September 7, 2026 19:32

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 13dd31c. Configure here.

protected readonly yearOptions = MENTORSHIP_TERM_YEAR_OPTIONS.map((option) => ({ ...option }));
protected readonly nameMax = MENTORSHIP_TERM_NAME_MAX;
protected readonly showErrors = signal(false);
protected readonly minApplicationDate = new Date();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today blocked as application date

Medium Severity

minApplicationDate is new Date() with the current clock time. Date-only validation treats today as valid, but a datetime minDate can make today's midnight earlier than now and disable today in the calendar. The custom-prerequisite picker zeros the time; this dialog does not.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 13dd31c. Configure here.

{ startDate, endDate, applicationStartDate, applicationEndDate },
new Date(),
this.data.mode === 'edit' ? this.data.term : undefined
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Term edit vs step date mismatch

Medium Severity

The term dialog calls getMentorshipTermDateErrors with the existing term as original, so unchanged past application dates are allowed. Setup Next calls the same helper without original, so those dates fail. Default and imported terms use applicationStartDate 2026-12-01, which becomes past after that day and leaves the wizard unable to continue from a successful Save.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 13dd31c. Configure here.

options.unshift({ id: selectedId, name: selectedId, value: selectedId, label: selectedId });
}
return options;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Selected project shows raw ID

Medium Severity

When the selected projectId is missing from the current LF project page or filter results, projectOptions reinserts it with label set to the raw id. After a search or a first-page reload, the required project field shows an opaque id instead of the project name.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 13dd31c. Configure here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 47 out of 47 changed files in this pull request and generated 7 comments.

Suppressed comments (1)

apps/lfx-one/src/server/services/mentorship.service.ts:131

  • The availability check ignores slug collisions even though enrollment rejects them. For example, after Foo Bar exists, checking Foo-Bar returns available, but submission fails because both names produce foo-bar. Apply the same normalized-name-or-slug predicate in both paths so the advisory result matches what creation accepts.

Comment on lines +88 to +90
/**
* Payload collected by the enroll wizard and POSTed to `/api/mentorship/programs`.
* Logo file bytes stay client-side; only `logoFileName` is sent to the BFF.
protected readonly yearOptions = MENTORSHIP_TERM_YEAR_OPTIONS.map((option) => ({ ...option }));
protected readonly nameMax = MENTORSHIP_TERM_NAME_MAX;
protected readonly showErrors = signal(false);
protected readonly minApplicationDate = new Date();
Comment on lines +53 to +57
public isProgramNameAvailable(name: string): Observable<MentorshipNameAvailability> {
return this.http.get<MentorshipNameAvailability>('/api/mentorship/programs/name-available', { params: new HttpParams().set('name', name) }).pipe(
take(1),
catchError(() => of({ available: true }))
);
Comment on lines +71 to +75
public getCiiBadge(projectId: string): Observable<MentorshipCiiBadge | null> {
return this.http.get<MentorshipCiiBadge>(`/api/mentorship/cii/${encodeURIComponent(projectId)}`).pipe(
take(1),
catchError(() => of(null))
);
Comment on lines +322 to +328
export const MENTORSHIP_DEFAULT_TERM: MentorshipProgramTerm = {
id: 'term-1-2027',
name: 'Term 1 - 2027',
startDate: '2027-03-01',
endDate: '2027-05-01',
applicationStartDate: '2026-12-01',
applicationEndDate: '2027-02-28',
Comment on lines +39 to +45
<input
type="url"
[id]="'challenge-url-' + item.id"
class="max-w-md w-full rounded-md border border-gray-300 px-3 py-2 text-sm"
[value]="item.challengeUrl"
placeholder="https://www.github.com/..."
(input)="onChallengeUrlInput(item.id, $event)" />
Comment on lines +96 to +101
<p-checkbox
[formControl]="termsControl()"
[binary]="true"
inputId="mentorship-enroll-terms"
(onChange)="onTermsAcceptedChange($event)"
data-testid="mentorship-enroll-terms" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deploy-preview do-not-merge Indicates that the pull request should NOT be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants