feat(mentorship): add mentorship module with routing, components, and services - #2223
feat(mentorship): add mentorship module with routing, components, and services#2223Sameh16 wants to merge 9 commits into
Conversation
… 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>
PR SummaryMedium Risk Overview 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 The BFF mounts Shared UI gains optional Reviewed by Cursor Bugbot for commit 13dd31c. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Essentials Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
🚀 Deployment StatusYour branch has been deployed to: https://ui-pr-2223.dev.v2.cluster.linuxfound.info Deployment Details:
The deployment will be automatically removed when this PR is closed. |
There was a problem hiding this comment.
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.
- 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>
There was a problem hiding this comment.
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, queryingGridFlow Time Series Ingestion Pipelinereturnsavailable: 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,
getMentorshipTermDateErrorsrejects 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-75requires onestartOperationper endpoint and reserves service logging fordebug/info. Remove the service-level start/success pairs (or replace them with step-level logging only where useful).
…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>
There was a problem hiding this comment.
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: trueon 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-zsort after today and pass the server-side enrollment validation even though the interface requiresYYYY-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 Barexists, checkingFoo-Barreports 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);
- 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>
There was a problem hiding this comment.
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
projectIdis nonblank, then this fallback accepts any arbitrary value and records it as the sponsoring project name. A direct POST such asprojectId: "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 anAdminheading. 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.tscovering unauthenticated requests, malformed query/body input, successful enrollment, and forwarding service errors tonext.
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
enrollProgramalso rejects slug collisions. For example, an existingA-BmakesA Breport available here but fail with 409 on submission because both slugify toa-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
nullused 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. Returnnullonly 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
Dateconstruction normalize malformed values instead of rejecting them:2026-02-31becomes March 3 and trailing junk is accepted.formatMentorshipShortMonthYearcan 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.tscovering 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-01or2026-09-01junkare accepted despite the function's documented contract. DownstreamlastDayOfMentorshipMonththen normalizes invalid months into a different year. Require an exact, realYYYY-MM-DDbefore returning its month/year.
const iso = /^(\d{4})-(\d{2})(?:-\d{2})?/.exec(trimmed);
if (iso) {
return { year: iso[1], month: iso[2] };
}
- 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>
There was a problem hiding this comment.
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 projectIdis 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 againstMOCK_MENTORSHIP_LF_PROJECTSand reject unknown IDs withServiceValidationErrorinstead 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-31becomes March 3).formatMentorshipShortMonthYearwill therefore display a plausible month for malformed API data instead of preserving or rejecting it. Reuse the exact calendar-date validator before constructing theDate.
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>
There was a problem hiding this comment.
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.parseIntaccepts malformed prefixes such aslimit=10oops,limit=1.5, andlimit=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) withServiceValidationError, as the existinguser.controller.ts:77-91pagination 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']);
- 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>
There was a problem hiding this comment.
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, keepstartOperation/successin controllers and usedebug/infofor 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>
There was a problem hiding this comment.
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
projectIdis 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
createEmptyMentorshipEnrollFormstarts 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-junkare accepted andlastDayOfMentorshipMonthsilently normalizes them into a different year. This contradicts the helper's documentedYYYY-MM-DDcontract 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
startDatebefore 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
Datenormalize them; for example,2026-02-31becomes March andformatMentorshipShortMonthYeardisplays a plausible but incorrect month. Validate the exact calendar date before constructing it, asformatIsoDateLabelalready 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>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
❌ 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(); |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit 13dd31c. Configure here.
| { startDate, endDate, applicationStartDate, applicationEndDate }, | ||
| new Date(), | ||
| this.data.mode === 'edit' ? this.data.term : undefined | ||
| ); |
There was a problem hiding this comment.
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)
Reviewed by Cursor Bugbot for commit 13dd31c. Configure here.
| options.unshift({ id: selectedId, name: selectedId, value: selectedId, label: selectedId }); | ||
| } | ||
| return options; | ||
| }); |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit 13dd31c. Configure here.
There was a problem hiding this comment.
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 Barexists, checkingFoo-Barreturns available, but submission fails because both names producefoo-bar. Apply the same normalized-name-or-slug predicate in both paths so the advisory result matches what creation accepts.
| /** | ||
| * 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(); |
| 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 })) | ||
| ); |
| public getCiiBadge(projectId: string): Observable<MentorshipCiiBadge | null> { | ||
| return this.http.get<MentorshipCiiBadge>(`/api/mentorship/cii/${encodeURIComponent(projectId)}`).pipe( | ||
| take(1), | ||
| catchError(() => of(null)) | ||
| ); |
| 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', |
| <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)" /> |
| <p-checkbox | ||
| [formControl]="termsControl()" | ||
| [binary]="true" | ||
| inputId="mentorship-enroll-terms" | ||
| (onChange)="onTermsAcceptedChange($event)" | ||
| data-testid="mentorship-enroll-terms" /> |


Overview
feat(mentorship): add mentorship module with routing, components, and services
MentorshipServiceto handle API interactions for mentorship programs.AdminComponentfor the mentorship admin page, including its HTML template.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
/mentorship/admin(sidebar-nav.service.ts)./mentorshippath and lazy-loaded mentorship routes inapp.routes.ts./adminand loading theAdminComponent.AdminComponentand template) as the initial frontend entry point. [1] [2]Frontend Service Layer
MentorshipServicein 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
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]/api/mentorshipin the main server configuration. [1] [2]