-
-
Notifications
You must be signed in to change notification settings - Fork 217
Created Module and program pages #1717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Summary by CodeRabbit
WalkthroughIntroduces mentorship program/module keys with slugging and uniqueness, extensive Strawberry GraphQL API for mentorship (nodes, queries, mutations), Algolia indexing for programs with cache clearing via signals, OWASP project queries additions, GraphQL schema wiring, and a comprehensive frontend: authentication role flags, admin/user mentorship pages, forms, components, queries/mutations, types/utils, middleware, and unit tests. Changes
Estimated code review effort🎯 5 (Critical) | ⏱️ ~90+ minutes Possibly related PRs
Suggested reviewers
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 31
🔭 Outside diff range comments (1)
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
44-50
: Consider eliminating duplicate GraphQL calls.The same
GITHUB_AUTH_MUTATION
is called in bothsignIn
andjwt
callbacks, which is redundant and could cause performance issues.Consider removing the GraphQL call from the
signIn
callback since thejwt
callback already handles this:async signIn({ account }) { if (account?.provider === 'github' && account.access_token) { - try { - const { data } = await apolloClient.mutate({ - mutation: GITHUB_AUTH_MUTATION, - variables: { - accessToken: account.access_token, - }, - }) - if (!data?.githubAuth?.authUser) throw new Error('User sync failed') - return true - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (error) { - throw new Error('GitHub authentication failed') - } + return true } return true },Also applies to: 66-79
🧹 Nitpick comments (26)
frontend/src/utils/constants.ts (1)
15-18
: Fix text capitalization for consistency.The navigation link text should be capitalized to match the existing pattern. All other navigation items use proper capitalization (Community, Projects, Contribute, About).
{ - text: 'mentorship', + text: 'Mentorship', href: '/mentorship/programs', },frontend/src/components/Card.tsx (1)
79-86
: Consider adding error handling for invalid dates.The timeline feature implementation looks good, but consider adding error handling for invalid dates to prevent potential runtime errors.
{/* Timeline Section (Optional) */} {timeline?.start && timeline?.end && ( <div className="mt-2 text-sm text-gray-500 dark:text-gray-400"> <FontAwesomeIcon icon={faCalendarDays} className="mr-1 inline-block h-4 w-4" /> - {new Date(timeline.start).toLocaleDateString()} →{' '} - {new Date(timeline.end).toLocaleDateString()} + {new Date(timeline.start).toLocaleDateString()} →{' '} + {new Date(timeline.end).toLocaleDateString()} </div> )}Or add validation:
+ {timeline?.start && timeline?.end && !isNaN(new Date(timeline.start).getTime()) && !isNaN(new Date(timeline.end).getTime()) && ( - {timeline?.start && timeline?.end && (backend/apps/nest/graphql/mutations/user.py (1)
66-69
: Consider adding logging for role changes.When a user's role is updated, it would be helpful to log this change for audit purposes.
if not created and auth_user.role != role: + logger.info( + "User role changed: %s (%s -> %s)", + auth_user.username, + auth_user.role, + role + ) auth_user.role = role auth_user.save(update_fields=["role"])frontend/src/utils/helpers/apolloClient.ts (1)
40-42
: Clarify variable naming for better understanding.The variable name
apolloClientPromise
is misleading sincecreateApolloClient()
returns anApolloClient
instance ornull
, not a Promise.-const apolloClientPromise = createApolloClient() +const apolloClient = createApolloClient() -export default apolloClientPromise +export default apolloClientIf the intention is to make this async in the future, consider documenting that or implementing it now.
frontend/src/app/mentorship/programs/create/page.tsx (1)
59-70
: Optimize array processing for better performance.The comma-separated string processing could be more efficient and handle edge cases better.
- tags: formData.tags - .split(',') - .map((t) => t.trim()) - .filter(Boolean), - domains: formData.domains - .split(',') - .map((d) => d.trim()) - .filter(Boolean), - adminLogins: formData.adminLogins - .split(',') - .map((login) => login.trim()) - .filter(Boolean), + tags: formData.tags.split(',').map(t => t.trim()).filter(Boolean), + domains: formData.domains.split(',').map(d => d.trim()).filter(Boolean), + adminLogins: formData.adminLogins.split(',').map(login => login.trim()).filter(Boolean),Consider extracting this logic into a utility function since it's reused multiple times.
backend/apps/mentorship/graphql/queries/program.py (2)
16-31
: Optimize database queries and add input validation.The query filtering logic is good, but consider adding input validation and potentially using Q objects for more complex filters.
@strawberry.field def all_programs( self, page: int = 1, search: str = "", mentor_username: str | None = None, ) -> PaginatedPrograms: """Get all programs with optional pagination, search, and mentor filtering.""" + # Validate pagination parameters + if page < 1: + page = 1 + if search and len(search.strip()) < 2: + search = "" # Avoid inefficient short searches + queryset = Program.objects.prefetch_related("admins__github_user").all()
39-59
: Extract ProgramNode creation logic to reduce duplication.The
ProgramNode
creation logic is duplicated betweenall_programs
andprogram
methods. Consider extracting it into a helper method.+ def _create_program_node(self, program: Program) -> ProgramNode: + """Create a ProgramNode from a Program instance.""" + return ProgramNode( + id=program.id, + key=program.key, + name=program.name, + description=program.description, + admins=list(program.admins.all()), + domains=program.domains, + ended_at=program.ended_at, + experience_levels=( + [ExperienceLevelEnum(level) for level in program.experience_levels] + if program.experience_levels + else [] + ), + mentees_limit=program.mentees_limit, + started_at=program.started_at, + status=ProgramStatusEnum(program.status), + tags=program.tags, + ) - programs = [ - ProgramNode( - # ... existing fields - ) - for program in programs_qs - ] + programs = [self._create_program_node(program) for program in programs_qs]frontend/src/app/mentorship/programs/[programKey]/edit/page.tsx (1)
99-149
: Extract shared form submission logic.The form submission logic is nearly identical to the create page. Consider extracting this into a shared utility function or custom hook.
Create a shared utility function:
// utils/programFormHelpers.ts export const prepareProgramInput = (formData: any, programKey?: string) => { const input = { name: formData.name, description: formData.description, menteesLimit: Number(formData.menteesLimit), startedAt: formData.startedAt, endedAt: formData.endedAt, experienceLevels: formData.experienceLevels, tags: formData.tags.split(',').map(t => t.trim()).filter(Boolean), domains: formData.domains.split(',').map(d => d.trim()).filter(Boolean), adminLogins: formData.adminLogins.split(',').map(login => login.trim()).filter(Boolean), status: formData.status, } if (programKey) { input.key = programKey } return input }This would reduce code duplication and make maintenance easier.
frontend/src/app/mentorship/programs/[programKey]/page.tsx (1)
72-81
: Consider using ProgramStatusEnum constant instead of hardcoded string.The publish mutation uses a hardcoded 'PUBLISHED' string instead of the enum value for consistency.
await updateProgram({ variables: { inputData: { key: program.key, name: program.name, - status: 'PUBLISHED', + status: ProgramStatusEnum.PUBLISHED, }, }, })frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx (2)
50-72
: Optimize access control logic with early returns.The access control logic runs even when the query is still loading or session is not authenticated. Consider adding early returns for better performance.
useEffect(() => { - if (sessionStatus !== 'authenticated' || queryLoading) return + if (sessionStatus !== 'authenticated') return + if (queryLoading || !data) return const programAdmins = data?.program?.admins || [] const isAdmin = programAdmins.some( (admin: { login: string }) => admin.login === currentUserLogin )
105-118
: Consider adding input validation for comma-separated values.The string splitting logic assumes well-formed input. Consider adding validation to handle edge cases.
domains: formData.domains .split(',') - .map((d: string) => d.trim()) - .filter(Boolean), + .map((d: string) => d.trim()) + .filter((d: string) => d.length > 0),frontend/src/components/ModuleCard.tsx (2)
53-55
: Consider using Next.js router.push with relative path construction.The current navigation depends on
window.location.pathname
which may not be reliable in all environments.const handleClick = () => { - router.push(`${window.location.pathname}/modules/${details.key}`) + router.push(`modules/${details.key}`) }
78-87
: Add input validation to the duration calculation function.The function doesn't validate that the dates are valid or that end date is after start date.
export const getSimpleDuration = (start: string, end: string): string => { const startDate = new Date(start) const endDate = new Date(end) + if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { + return 'Invalid dates' + } const ms = endDate.getTime() - startDate.getTime() + if (ms < 0) { + return 'Invalid duration' + } + const days = Math.floor(ms / (1000 * 60 * 60 * 24)) const weeks = Math.ceil(days / 7) return `${weeks} week${weeks !== 1 ? 's' : ''}` }frontend/src/app/mentorship/programs/[programKey]/createModule/page.tsx (3)
37-37
: Consider adding null safety for session data.The type assertion could fail if session data doesn't match expected structure.
- const currentUserLogin = (sessionData as SessionWithRole)?.user?.username || '' + const currentUserLogin = (sessionData as SessionWithRole | null)?.user?.username || ''
76-90
: Consider adding validation for array parsing.The comma-separated value parsing could benefit from additional validation to handle edge cases.
domains: formData.domains .split(',') .map((d) => d.trim()) - .filter(Boolean), + .filter((d) => d.length > 0), tags: formData.tags .split(',') .map((t) => t.trim()) - .filter(Boolean), + .filter((t) => t.length > 0),
94-103
: Improve error handling specificity.The error handling could be more specific about different types of errors.
} catch (err) { + const errorMessage = err instanceof Error ? err.message : 'Something went wrong while creating the module.' addToast({ title: 'Creation Failed', - description: err.message || 'Something went wrong while creating the module.', + description: errorMessage, color: 'danger', variant: 'solid', timeout: 3000, shouldShowTimeoutProgress: true, }) }frontend/src/app/mentorship/programs/page.tsx (2)
44-55
: Consider optimizing URL update logic.The URL synchronization could be optimized to avoid unnecessary comparisons.
const params = new URLSearchParams() if (searchQuery) params.set('q', searchQuery) if (page > 1) params.set('page', page.toString()) const queryString = params.toString() const newUrl = queryString ? `?${queryString}` : window.location.pathname - if (window.location.search !== (queryString ? `?${queryString}` : '')) { + const currentSearch = window.location.search + const newSearch = queryString ? `?${queryString}` : '' + if (currentSearch !== newSearch) { router.push(newUrl, { scroll: false }) }
128-131
: Consider preserving scroll position on page change.The scroll behavior on page change could be smoother.
onPageChange={(p) => { setPage(p) - window.scrollTo({ top: 0, behavior: 'auto' }) + window.scrollTo({ top: 0, behavior: 'smooth' }) }}backend/apps/mentorship/graphql/nodes/program.py (1)
21-26
: Consider making datetime fields optional for better flexibility.The
ended_at
andstarted_at
fields are typed as required but programs might not always have defined end dates.- ended_at: datetime + ended_at: datetime | None = None - started_at: datetime + started_at: datetime | None = Nonefrontend/src/types/program.ts (1)
23-23
: Consider making menteesLimit nullable for consistency.The backend schema suggests this field can be null, but it's typed as required number here.
- menteesLimit: number + menteesLimit: number | nullfrontend/src/components/mainmoduleCard.tsx (2)
219-219
: Use router navigation instead of history.back().For consistency with Next.js routing patterns, use router navigation.
+ import { useRouter } from 'next/navigation' + + const router = useRouter() + <button type="button" - onClick={() => history.back()} + onClick={() => router.back()} className="rounded-lg border px-6 py-3 font-medium text-gray-600 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700" > Cancel </button>
182-195
: Add validation hint for Project ID field.The Project ID field is required but lacks guidance on what constitutes a valid ID.
<label htmlFor="projectId" className="mb-2 block text-sm font-medium"> - Project ID + Project ID * </label> <input id="projectId" type="text" name="projectId" value={formData.projectId} onChange={handleInputChange} - placeholder="Project UUID" + placeholder="Enter the project UUID" required className="w-full rounded-lg border-2 bg-gray-50 px-4 py-3 text-gray-800 focus:outline-none dark:bg-gray-800 dark:text-gray-200" /> + <p className="mt-1 text-xs text-gray-500 dark:text-gray-400"> + Enter a valid project UUID from the available projects. + </p>frontend/src/components/programCard.tsx (2)
276-280
: Consider using router navigation instead of history.back().Using
history.back()
might not navigate to the expected page if users arrive via direct link or bookmarks. Consider using Next.js router for predictable navigation.+import { useRouter } from 'next/navigation' const ProgramForm = ({ ... }) => { + const router = useRouter() // ... rest of component <button type="button" - onClick={() => history.back()} + onClick={() => router.push('/mentorship/programs')} className="rounded-lg border px-6 py-3 font-medium text-gray-600 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700" > Cancel </button>
226-248
: Add guidance for comma-separated inputs.The tags and domains fields accept comma-separated values but lack user guidance and validation. Consider adding helper text and input validation.
<label htmlFor="program-tags" className="mb-2 block text-sm font-medium"> Tags + <span className="ml-1 text-xs text-gray-500">(comma-separated)</span> </label> <input id="program-tags" type="text" name="tags" value={formData.tags} onChange={handleInputChange} - placeholder="javascript, react" + placeholder="e.g., javascript, react, typescript" className="w-full rounded-lg border-2 bg-gray-50 px-4 py-3 text-gray-800 focus:outline-none dark:bg-gray-800 dark:text-gray-200" />Apply similar changes to the domains field.
frontend/src/components/CardDetailsPage.tsx (1)
107-116
: Use consistent ActionButton component for module editing.The module edit button uses different styling and implementation compared to program buttons. Consider using the same ActionButton component for consistency.
{type === 'module' && ( - <button - className="flex items-center justify-center gap-2 text-nowrap rounded-md border border-[#0D6EFD] bg-transparent px-2 py-2 text-[#0D6EFD] text-blue-600 transition-all hover:bg-[#0D6EFD] hover:text-white dark:border-sky-600 dark:text-sky-600 dark:hover:bg-sky-100" + <ActionButton onClick={() => { router.push(`${window.location.pathname}/edit`) }} > Edit Module - </button> + </ActionButton> )}frontend/src/server/queries/programsQueries.ts (1)
118-131
: Consider removing redundant query.The
GET_PROGRAM_ADMIN_DETAILS
query fetches a subset of data already available inGET_PROGRAM_DETAILS
. Consider reusing the existing query to reduce code duplication and network requests.If you need a lighter query for performance reasons, consider using GraphQL fragments to share field selections between queries instead of duplicating them.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (48)
backend/apps/mentorship/graphql/mutations/__init__.py
(1 hunks)backend/apps/mentorship/graphql/mutations/module.py
(1 hunks)backend/apps/mentorship/graphql/mutations/program.py
(1 hunks)backend/apps/mentorship/graphql/nodes/__init__.py
(1 hunks)backend/apps/mentorship/graphql/nodes/enum.py
(1 hunks)backend/apps/mentorship/graphql/nodes/mentor.py
(1 hunks)backend/apps/mentorship/graphql/nodes/modules.py
(1 hunks)backend/apps/mentorship/graphql/nodes/program.py
(1 hunks)backend/apps/mentorship/graphql/queries/__init__.py
(1 hunks)backend/apps/mentorship/graphql/queries/module.py
(1 hunks)backend/apps/mentorship/graphql/queries/program.py
(1 hunks)backend/apps/mentorship/migrations/0002_module_key_program_key.py
(1 hunks)backend/apps/mentorship/models/module.py
(2 hunks)backend/apps/mentorship/models/program.py
(2 hunks)backend/apps/mentorship/utils/user.py
(1 hunks)backend/apps/nest/graphql/mutations/user.py
(2 hunks)backend/apps/nest/graphql/nodes/user.py
(1 hunks)backend/apps/nest/migrations/0002_user_role.py
(1 hunks)backend/apps/nest/models/user.py
(2 hunks)backend/apps/owasp/models/__init__.py
(1 hunks)backend/pyproject.toml
(1 hunks)backend/settings/graphql.py
(1 hunks)frontend/package.json
(2 hunks)frontend/src/app/api/auth/[...nextauth]/route.ts
(4 hunks)frontend/src/app/mentorship/programs/[programKey]/createModule/page.tsx
(1 hunks)frontend/src/app/mentorship/programs/[programKey]/edit/page.tsx
(1 hunks)frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
(1 hunks)frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
(1 hunks)frontend/src/app/mentorship/programs/[programKey]/page.tsx
(1 hunks)frontend/src/app/mentorship/programs/create/page.tsx
(1 hunks)frontend/src/app/mentorship/programs/page.tsx
(1 hunks)frontend/src/components/Card.tsx
(3 hunks)frontend/src/components/CardDetailsPage.tsx
(9 hunks)frontend/src/components/InfoItem.tsx
(1 hunks)frontend/src/components/ModuleCard.tsx
(1 hunks)frontend/src/components/SearchPageLayout.tsx
(1 hunks)frontend/src/components/mainmoduleCard.tsx
(1 hunks)frontend/src/components/programCard.tsx
(1 hunks)frontend/src/server/apolloClient.ts
(1 hunks)frontend/src/server/queries/moduleQueries.ts
(1 hunks)frontend/src/server/queries/programsQueries.ts
(1 hunks)frontend/src/types/card.ts
(3 hunks)frontend/src/types/contributor.ts
(1 hunks)frontend/src/types/program.ts
(1 hunks)frontend/src/utils/capitalize.ts
(1 hunks)frontend/src/utils/constants.ts
(1 hunks)frontend/src/utils/dateFormatter.ts
(1 hunks)frontend/src/utils/helpers/apolloClient.ts
(3 hunks)
🧰 Additional context used
🧠 Learnings (9)
frontend/src/types/contributor.ts (1)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1703
File: frontend/src/types/healthMetrics.ts:6-24
Timestamp: 2025-07-03T03:03:38.713Z
Learning: In TypeScript, optional properties without explicit type annotations (like `fill?` and `meta?`) implicitly default to the `any` type. This is valid syntax and doesn't cause compilation errors. `fill?` is equivalent to `fill?: any`.
frontend/src/components/Card.tsx (2)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
backend/apps/mentorship/graphql/nodes/enum.py (1)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1676
File: backend/apps/owasp/graphql/filters/project_health_metrics.py:17-22
Timestamp: 2025-06-29T00:41:32.198Z
Learning: In the OWASP Nest codebase, when implementing GraphQL filters that convert string values to enums (like ProjectLevel), do not catch ValueError exceptions for invalid values. Let the errors propagate to provide proper error responses to GraphQL clients rather than silently ignoring invalid input.
backend/apps/nest/graphql/mutations/user.py (1)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1676
File: backend/apps/owasp/graphql/filters/project_health_metrics.py:17-22
Timestamp: 2025-06-29T00:41:32.198Z
Learning: In the OWASP Nest codebase, when implementing GraphQL filters that convert string values to enums (like ProjectLevel), do not catch ValueError exceptions for invalid values. Let the errors propagate to provide proper error responses to GraphQL clients rather than silently ignoring invalid input.
frontend/src/types/card.ts (2)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
frontend/src/components/SearchPageLayout.tsx (1)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1703
File: frontend/src/types/healthMetrics.ts:6-24
Timestamp: 2025-07-03T03:03:38.713Z
Learning: In TypeScript, optional properties without explicit type annotations (like `fill?` and `meta?`) implicitly default to the `any` type. This is valid syntax and doesn't cause compilation errors. `fill?` is equivalent to `fill?: any`.
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
Learnt from: Rajgupta36
PR: OWASP/Nest#1634
File: backend/apps/nest/graphql/mutations/user.py:24-48
Timestamp: 2025-06-18T21:00:33.024Z
Learning: GitHub access tokens use specific prefixes to identify token types: OAuth tokens start with `gho_`, classic personal access tokens with `ghp_`, fine-grained personal access tokens with `github_pat_`, GitHub App user tokens with `ghu_`, GitHub App installation tokens with `ghs_`, and refresh tokens with `ghr_`. This is documented in GitHub's official authentication documentation and engineering blog.
frontend/src/components/ModuleCard.tsx (2)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
frontend/src/components/CardDetailsPage.tsx (2)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
🧬 Code Graph Analysis (17)
backend/apps/owasp/models/__init__.py (3)
backend/apps/owasp/admin.py (1)
project
(186-188)backend/apps/github/models/repository.py (1)
project
(158-160)backend/apps/owasp/models/project.py (1)
Project
(26-354)
backend/apps/mentorship/graphql/mutations/__init__.py (2)
backend/apps/mentorship/graphql/mutations/module.py (1)
ModuleMutation
(19-170)backend/apps/mentorship/graphql/mutations/program.py (1)
ProgramMutation
(17-151)
backend/apps/nest/graphql/nodes/user.py (1)
backend/apps/nest/models/user.py (1)
User
(9-47)
backend/apps/mentorship/models/module.py (3)
backend/apps/mentorship/models/common/experience_level.py (1)
ExperienceLevel
(6-25)backend/apps/mentorship/models/common/matching_attributes.py (1)
MatchingAttributes
(6-24)backend/apps/mentorship/models/common/start_end_range.py (1)
StartEndRange
(6-13)
backend/apps/mentorship/models/program.py (3)
backend/apps/mentorship/models/common/experience_level.py (1)
ExperienceLevel
(6-25)backend/apps/mentorship/models/common/matching_attributes.py (1)
MatchingAttributes
(6-24)backend/apps/mentorship/models/common/start_end_range.py (1)
StartEndRange
(6-13)
frontend/src/server/apolloClient.ts (1)
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
session
(84-96)
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (6)
frontend/src/types/program.ts (1)
Module
(32-44)frontend/src/server/queries/moduleQueries.ts (1)
GET_PROGRAM_ADMINS_AND_MODULES
(91-118)frontend/src/app/global-error.tsx (2)
handleAppError
(66-86)ErrorDisplay
(28-51)frontend/src/utils/capitalize.ts (1)
capitalize
(1-4)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)frontend/src/components/ModuleCard.tsx (1)
getSimpleDuration
(78-87)
backend/apps/nest/graphql/mutations/user.py (2)
backend/apps/nest/models/user.py (1)
User
(9-47)backend/apps/github/models/user.py (1)
User
(14-134)
frontend/src/utils/helpers/apolloClient.ts (2)
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
session
(84-96)frontend/src/utils/utility.ts (1)
getCsrfToken
(51-65)
backend/apps/mentorship/graphql/queries/program.py (4)
backend/apps/mentorship/graphql/nodes/enum.py (2)
ExperienceLevelEnum
(9-15)ProgramStatusEnum
(19-24)backend/apps/mentorship/graphql/nodes/program.py (2)
PaginatedPrograms
(30-35)ProgramNode
(12-26)backend/apps/mentorship/models/program.py (1)
Program
(16-79)frontend/src/types/program.ts (1)
Program
(16-30)
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
frontend/src/server/apolloClient.ts (1)
apolloClient
(42-43)
backend/apps/mentorship/graphql/nodes/program.py (2)
backend/apps/mentorship/graphql/nodes/enum.py (2)
ExperienceLevelEnum
(9-15)ProgramStatusEnum
(19-24)backend/apps/mentorship/graphql/nodes/mentor.py (2)
MentorNode
(7-25)name
(18-20)
frontend/src/components/ModuleCard.tsx (5)
frontend/src/types/program.ts (1)
Module
(32-44)frontend/src/components/TruncatedText.tsx (1)
TruncatedText
(3-45)frontend/src/components/InfoItem.tsx (1)
TextInfoItem
(33-48)frontend/src/utils/capitalize.ts (1)
capitalize
(1-4)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/types/program.ts (4)
backend/apps/mentorship/graphql/nodes/enum.py (2)
ExperienceLevelEnum
(9-15)ProgramStatusEnum
(19-24)backend/apps/mentorship/models/program.py (1)
Program
(16-79)frontend/src/types/contributor.ts (1)
Contributor
(1-8)backend/apps/mentorship/models/module.py (1)
Module
(15-75)
backend/apps/mentorship/graphql/mutations/module.py (5)
backend/apps/mentorship/graphql/nodes/modules.py (3)
CreateModuleInput
(31-43)ModuleNode
(13-27)UpdateModuleInput
(47-59)backend/apps/mentorship/models/mentor.py (1)
Mentor
(11-40)backend/apps/mentorship/models/module.py (2)
Module
(15-75)save
(68-75)backend/apps/mentorship/models/program.py (1)
Program
(16-79)backend/apps/mentorship/utils/user.py (1)
get_authenticated_user
(10-37)
frontend/src/components/CardDetailsPage.tsx (1)
frontend/src/types/program.ts (1)
SessionWithRole
(46-52)
backend/apps/mentorship/graphql/nodes/modules.py (4)
backend/apps/mentorship/graphql/nodes/enum.py (1)
ExperienceLevelEnum
(9-15)backend/apps/mentorship/graphql/nodes/mentor.py (2)
MentorNode
(7-25)name
(18-20)backend/apps/mentorship/graphql/queries/program.py (1)
program
(68-92)backend/apps/mentorship/graphql/nodes/program.py (1)
ProgramNode
(12-26)
🪛 Flake8 (7.2.0)
backend/apps/owasp/models/__init__.py
[error] 1-1: '.project.Project' imported but unused
(F401)
backend/apps/mentorship/graphql/mutations/__init__.py
[error] 3-3: '.module.ModuleMutation' imported but unused
(F401)
[error] 4-4: '.program.ProgramMutation' imported but unused
(F401)
backend/apps/mentorship/graphql/queries/__init__.py
[error] 3-3: '.module.ModuleQuery' imported but unused
(F401)
[error] 4-4: '.program.ProgramQuery' imported but unused
(F401)
🪛 Biome (1.9.4)
frontend/src/components/CardDetailsPage.tsx
[error] 102-102: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 92-92: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 96-96: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 102-102: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
🔇 Additional comments (49)
backend/apps/mentorship/graphql/nodes/__init__.py (1)
1-2
: LGTM!Standard Python package initialization file with appropriate comment.
frontend/package.json (2)
25-25
: LGTM!Appropriate addition of date picker dependency for the mentorship features.
34-34
: LGTM!Good addition of internationalized date handling utility to support the date picker functionality.
backend/apps/owasp/models/__init__.py (1)
1-1
: LGTM!This import follows the standard Python pattern of exposing models at the package level. The static analysis warning is a false positive - this is the correct way to make the Project model accessible from the package.
backend/pyproject.toml (1)
134-136
: LGTM!Reasonable addition of exception handling linting rules to ignore. These rules (EM101, TRY002, TRY003) can be overly restrictive in Django applications and the flexibility will help maintain code quality without unnecessary noise.
backend/apps/nest/graphql/nodes/user.py (1)
8-8
: LGTM! Role field exposure through GraphQL.The addition of the "role" field to the GraphQL node correctly exposes the user role information that was added to the User model, enabling role-based functionality in the frontend.
frontend/src/components/SearchPageLayout.tsx (1)
15-15
: LGTM! Flexible empty state handling.Making the
empty
prop optional improves component flexibility, allowing consumers to omit the empty state message when not needed. The usage on line 58 handles undefined values gracefully.backend/apps/mentorship/models/module.py (1)
8-12
: LGTM - Import statement formatting improved.The multi-line import style enhances readability and follows Python best practices.
frontend/src/utils/capitalize.ts (1)
3-3
: Widespread usage ofcapitalize
—please verify behavior changeThe updated implementation lowercases the entire remainder of the string, which is indeed a breaking change from the previous behavior that preserved the original casing. We found the following call sites and imports across the codebase:
• frontend/src/utils/capitalize.ts
• frontend/tests/unit/utils/capitalize.test.ts
• frontend/src/app/page.tsx
• frontend/src/app/projects/[projectKey]/page.tsx
• frontend/src/app/mentorship/programs/[programKey]/page.tsx
• frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
• frontend/src/components/TopContributorsList.tsx
• frontend/src/components/ModuleCard.tsx
• frontend/src/components/CardDetailsPage.tsx
• frontend/src/components/BreadCrumbs.tsxNext steps:
- Confirm that all affected snapshots/tests have been updated to expect the new lowercase remainder.
- Verify no UI labels or dynamic strings rely on preserving original casing beyond the first character (e.g., acronyms or mixed-case identifiers).
- If any call site requires the old behavior, consider adding a separate helper or flag to preserve casing.
frontend/src/types/contributor.ts (1)
3-3
: LGTM - Improved type flexibility.Making
contributionsCount
andprojectKey
optional enhances type safety by accurately reflecting their actual usage patterns across the mentorship components where these fields may not always be present.Also applies to: 6-6
backend/apps/mentorship/models/program.py (1)
9-13
: LGTM - Import statement formatting improved.The multi-line import style enhances readability and follows Python best practices.
backend/apps/nest/migrations/0002_user_role.py (1)
1-23
: LGTM - Well-formed Django migration.The migration correctly adds a
role
field with appropriate choices, default value, and constraints. The field design supports the role-based user management system being implemented.frontend/src/components/InfoItem.tsx (1)
33-48
: LGTM! Clean and well-structured component.The
TextInfoItem
component is well-implemented with proper TypeScript typing, consistent styling, and clear purpose. The component follows React best practices and integrates well with the existing codebase.frontend/src/components/Card.tsx (1)
1-1
: LGTM! Good addition of calendar icon import.The import of
faCalendarDays
from FontAwesome regular icons is appropriate for the timeline feature.backend/apps/nest/models/user.py (2)
12-15
: LGTM! Well-structured role choices definition.The
ROLE_CHOICES
tuple is properly defined with clear key-value pairs for "contributor" and "mentor" roles.
33-39
: LGTM! Proper Django model field implementation.The
role
field is correctly implemented with appropriate constraints, default value, and choices. The field configuration follows Django best practices.backend/apps/mentorship/graphql/nodes/enum.py (1)
8-24
: Well-implemented enum definitions.The enum implementations follow proper Python enum patterns with consistent lowercase string values and correct Strawberry GraphQL decorators. The structure aligns with GraphQL best practices for strongly typed enum fields.
backend/apps/mentorship/utils/user.py (1)
16-16
: Python Version Compatibility ConfirmedThe CI workflows specify
python-version: '3.13'
(both in.github/workflows/test-schema.yaml
andrun-ci-cd.yaml
), which is ≥ 3.9. Sincestr.removeprefix
was introduced in Python 3.9, it’s safe to use here—no changes needed.backend/settings/graphql.py (2)
6-8
: Clean GraphQL schema integration.The import structure and module organization follow good practices for separating concerns between different app modules.
12-25
: Proper strawberry GraphQL decorators and inheritance.The use of
@strawberry.type
decorators and multi-line inheritance improves readability and follows strawberry conventions correctly.frontend/src/app/mentorship/programs/[programKey]/page.tsx (1)
108-117
: LGTM! Well-structured program details formatting.The program details are properly formatted with null safety and appropriate data transformations.
frontend/src/types/card.ts (2)
11-11
: LGTM! Proper import of Module type.The Module type import is correctly added to support the new modules property.
26-29
: LGTM! Well-structured type extensions.The new optional properties are consistently added following the existing pattern and support the mentorship program functionality.
Also applies to: 44-44, 49-50, 52-53, 60-60, 67-67
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx (1)
94-126
: LGTM! Good error handling and form submission logic.The form submission properly handles errors and transforms the data correctly for the GraphQL mutation.
frontend/src/components/ModuleCard.tsx (1)
58-74
: LGTM! Well-structured module item display.The ModuleItem component properly displays module information with appropriate styling and accessibility.
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
88-94
: LGTM! Proper session data assignment.The session callback correctly assigns role and username from the JWT token to the session object.
frontend/src/app/mentorship/programs/[programKey]/createModule/page.tsx (1)
42-64
: LGTM! Excellent access control implementation.The access control logic properly verifies admin privileges before allowing module creation, with appropriate user feedback and navigation.
frontend/src/app/mentorship/programs/page.tsx (2)
31-42
: LGTM! Proper debouncing implementation.The debounce setup with cleanup is correctly implemented to optimize search performance.
142-157
: LGTM! Excellent role-based UI controls.The mentor role checks and button logic are well-implemented with proper state management.
backend/apps/mentorship/graphql/nodes/program.py (3)
11-27
: LGTM! Well-structured GraphQL node definition.The ProgramNode type definition is comprehensive and properly typed with appropriate nullable fields.
29-36
: LGTM! Proper pagination structure.The PaginatedPrograms type follows standard pagination patterns with current page, total pages, and results.
46-51
: GraphQL input correctly mirrors non-nullable model fields
TheCreateProgramInput
requiresstarted_at
andended_at
, matching their definitions inStartEndRange
(non-nullableDateTimeField
s). Only if your intention is to allow draft programs without dates should you:
- update
backend/apps/mentorship/models/common/start_end_range.py
toDateTimeField(null=True, blank=True)
- make
started_at
and/orended_at
optional inCreateProgramInput
Otherwise, no changes are needed.
frontend/src/types/program.ts (3)
2-7
: LGTM! Enum values match backend schema.The ExperienceLevelEnum values are consistent with the backend enum definition.
9-13
: LGTM! Program status enum is properly defined.The ProgramStatusEnum values match the backend schema and provide type safety.
46-52
: LGTM! Session type extension is well-defined.The SessionWithRole type properly extends session data with role and username for access control.
frontend/src/components/mainmoduleCard.tsx (3)
25-30
: LGTM! Well-defined experience level options.The experience level constants are properly structured and match the backend enum values.
48-92
: LGTM! Excellent accessibility and responsive design.The form structure uses semantic HTML with proper labels, ARIA attributes, and responsive grid layout.
196-211
: LGTM! Proper conditional rendering for edit mode.The mentor login field is correctly shown only in edit mode, preventing confusion during module creation.
backend/apps/mentorship/graphql/nodes/modules.py (2)
12-28
: Well-structured GraphQL node definition!The
ModuleNode
type is properly defined with appropriate field types and optionality. The required fields (name, experience_level, mentors, dates) ensure data consistency while optional fields provide flexibility.
30-44
: Verify the project_id requirement inconsistency.The
project_id
field is required inCreateModuleInput
(line 41) but optional inModuleNode
(line 25). Please verify if this is intentional - modules might be created without projects after initial creation, or this could be an oversight.frontend/src/components/CardDetailsPage.tsx (1)
251-332
: Well-implemented program and module sections!The new sections for tags, domains, admins, mentors, and modules are properly structured with appropriate conditional rendering and responsive layouts. The reuse of existing components like ToggleableList and TopContributorsList maintains consistency.
frontend/src/server/queries/programsQueries.ts (1)
75-140
: Well-structured mutations with good separation of concerns!The mutations are properly organized with
UPDATE_PROGRAM
for general updates,CREATE_PROGRAM
for new programs, andUPDATE_PROGRAM_STATUS_MUTATION
for specific status changes. This separation provides flexibility and follows the single responsibility principle.backend/apps/mentorship/graphql/mutations/module.py (3)
45-50
: Fix date validation to ensure end date is strictly after start date.The current validation allows the end date to be equal to the start date, which doesn't make logical sense for a time range.
- if ( - input_data.ended_at is not None - and input_data.started_at is not None - and input_data.ended_at <= input_data.started_at - ): - raise Exception("End date must be after start date") + if ( + input_data.ended_at is not None + and input_data.started_at is not None + and input_data.ended_at < input_data.started_at + ): + raise Exception("End date must be after start date")Likely an incorrect or invalid review comment.
77-90
: Include project_id in the returned ModuleNode.The
ModuleNode
type includes aproject_id
field, but it's not being set in the return value. This is inconsistent with theget_module
query which correctly includes it.return ModuleNode( id=module.id, key=module.key, name=module.name, description=module.description, domains=module.domains, ended_at=module.ended_at, experience_level=module.experience_level, mentors=list(module.mentors.all()), program=module.program, - project_id=module.project.id if module.project else None, + project_id=module.project_id, started_at=module.started_at, tags=module.tags, )Likely an incorrect or invalid review comment.
111-116
: Fix date validation to ensure end date is strictly after start date.Same issue as in the create_module method - the validation should use
<
instead of<=
.- if ( - input_data.ended_at is not None - and input_data.started_at is not None - and input_data.ended_at <= input_data.started_at - ): - raise Exception("End date must be after start date") + if ( + input_data.ended_at is not None + and input_data.started_at is not None + and input_data.ended_at < input_data.started_at + ): + raise Exception("End date must be after start date")Likely an incorrect or invalid review comment.
backend/apps/mentorship/graphql/queries/module.py (1)
76-101
: LGTM!The
get_module
method is well-implemented with proper error handling, query optimization, and correctly includes all fields in the returnedModuleNode
.backend/apps/mentorship/graphql/mutations/program.py (3)
125-136
: Excellent use of case-insensitive GitHub user lookup!The implementation correctly uses
login__iexact
for case-insensitive matching, which is consistent throughout this file and prevents issues with username case variations.
29-30
: Fix date validation to ensure end date is strictly after start date.The validation allows the end date to equal the start date, which is inconsistent with the module mutations and doesn't make sense for a time range.
- if input_data.ended_at <= input_data.started_at: - raise Exception("End date must be after start date") + if input_data.ended_at < input_data.started_at: + raise Exception("End date must be after start date")Likely an incorrect or invalid review comment.
95-100
: Fix date validation to ensure end date is strictly after start date.Same issue as in create_program - should use
<
instead of<=
.- if ( - input_data.ended_at is not None - and input_data.started_at is not None - and input_data.ended_at <= input_data.started_at - ): - raise Exception("End date must be after start date") + if ( + input_data.ended_at is not None + and input_data.started_at is not None + and input_data.ended_at < input_data.started_at + ): + raise Exception("End date must be after start date")Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🔭 Outside diff range comments (1)
frontend/src/components/CardDetailsPage.tsx (1)
353-373
: Add TypeScript type annotation to exported component.The SocialLinks component should have proper type annotations for its props.
Apply this diff:
-export const SocialLinks = ({ urls }) => { +interface SocialLinksProps { + urls: string[] +} + +export const SocialLinks = ({ urls }: SocialLinksProps) => {
♻️ Duplicate comments (3)
frontend/src/server/apolloClient.ts (1)
4-4
: Replace getSession with getServerSession for server-side usage.This duplicates a previous review comment but remains unaddressed. The
getSession
function fromnext-auth/react
is optimized for client-side usage and makes an extra API call when used server-side. For server-side Apollo client setup, usegetServerSession
fromnext-auth/next
instead.#!/bin/bash # Description: Check if getServerSession is available and properly imported in the codebase # Expected: Find usage patterns and import statements for getServerSession rg -A 3 -B 3 "getServerSession" --type ts --type tsxfrontend/src/utils/helpers/apolloClient.ts (1)
21-24
: Improve type safety and error handling.The type casting
(session as SessionWithRole)?.accessToken
bypasses TypeScript's type safety. Consider using proper typing and add error handling for session retrieval.Apply this diff to improve type safety and error handling:
- const session = await getSession() - const accessToken = (session as SessionWithRole)?.accessToken - const csrfToken = await getCsrfToken() + try { + const session = await getSession() as SessionWithRole | null + const accessToken = session?.accessToken + const csrfToken = await getCsrfToken()Also ensure error handling is added at the end of the authLink:
headers: { ...headers, // eslint-disable-next-line @typescript-eslint/naming-convention Authorization: accessToken ? `Bearer ${accessToken}` : '', 'X-CSRFToken': csrfToken || '', }, + } catch (error) { + console.error('Failed to set authentication headers:', error) + return { headers } + }frontend/src/components/CardDetailsPage.tsx (1)
86-108
: Simplify admin authorization check and fix React children prop usage.The admin check can be simplified using optional chaining, and the ActionButton components should use JSX children instead of props.
Apply this diff:
-{type === 'program' && - admins?.some( - (admin) => admin.login === ((data as SessionWithRole)?.user?.username as string) - ) && ( +{type === 'program' && + admins?.some( + (admin) => admin.login === (data as SessionWithRole)?.user?.username + ) && ( <div className="flex gap-2"> <ActionButton onClick={() => { router.push(`${window.location.pathname}/edit`) }} - children="Edit Program" - /> + > + Edit Program + </ActionButton> <ActionButton - children=" Add Module" onClick={() => { router.push(`${window.location.pathname}/createModule`) }} - /> - {isDraft && ( - <ActionButton children="Publish" onClick={() => setPublish && setPublish()} /> - )} + > + Add Module + </ActionButton> + {isDraft && ( + <ActionButton onClick={() => setPublish?.()}> + Publish + </ActionButton> + )} </div> )}
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
backend/pyproject.toml
(1 hunks)frontend/package.json
(1 hunks)frontend/src/components/Card.tsx
(3 hunks)frontend/src/components/CardDetailsPage.tsx
(9 hunks)frontend/src/server/apolloClient.ts
(1 hunks)frontend/src/types/card.ts
(3 hunks)frontend/src/utils/helpers/apolloClient.ts
(3 hunks)
🧰 Additional context used
🧠 Learnings (3)
frontend/src/components/Card.tsx (2)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
frontend/src/types/card.ts (2)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
frontend/src/components/CardDetailsPage.tsx (2)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
🧬 Code Graph Analysis (4)
frontend/src/server/apolloClient.ts (2)
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
session
(84-96)frontend/src/types/program.ts (1)
SessionWithRole
(46-52)
frontend/src/utils/helpers/apolloClient.ts (3)
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
session
(84-96)frontend/src/types/program.ts (1)
SessionWithRole
(46-52)frontend/src/utils/utility.ts (1)
getCsrfToken
(51-65)
frontend/src/types/card.ts (7)
frontend/src/types/contributor.ts (1)
Contributor
(1-8)frontend/src/types/pullRequest.ts (1)
PullRequest
(3-10)frontend/src/types/issue.ts (1)
Issue
(3-19)frontend/src/types/milestone.ts (1)
Milestone
(3-15)frontend/src/types/release.ts (1)
Release
(3-14)frontend/src/types/project.ts (1)
RepositoryCardProps
(51-61)frontend/src/types/program.ts (1)
Module
(32-44)
frontend/src/components/CardDetailsPage.tsx (1)
frontend/src/types/program.ts (1)
SessionWithRole
(46-52)
🪛 Biome (1.9.4)
frontend/src/components/CardDetailsPage.tsx
[error] 105-105: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 95-95: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 99-99: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 105-105: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 111-116: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form
element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: CodeQL (javascript-typescript)
- GitHub Check: CodeQL (python)
🔇 Additional comments (7)
backend/pyproject.toml (1)
135-137
: Re-evaluate the blanket suppression of EM101, TRY002 & TRY003Adding these three rules to the global ignore list disables Ruff checks that flag dubious exception-handling patterns (
flake8-errmsg
andtryceratops
). That can silently permit:• Poorly formatted or non-parameterised error messages (EM101)
• Broad/bareexcept
blocks,pass
inexcept
, or forgottenraise … from exc
chains (TRY002/TRY003)Before merging, confirm that:
- The new mentorship code actually needs these rules suppressed across the entire codebase.
- Per-file/per-line
# noqa
or a narrower per-file ignore list wouldn’t be sufficient.Keeping these checks active helps catch real bugs early.
frontend/package.json (1)
24-26
: Peer-dependency compatibility verified for@heroui/[email protected]
No further action required—this release declares wide React support (including React 19):
- react: “>=18 || >=19.0.0-rc.0”
- react-dom: “>=18 || >=19.0.0-rc.0”
frontend/src/server/apolloClient.ts (3)
6-6
: Good type safety improvement.The import of
SessionWithRole
type addresses the previous concern about type safety. This is a good improvement over casting toany
.
12-13
: Session retrieval needs server-side optimization.While the type casting improvement is good, the underlying issue with using
getSession
in a server-side context remains. This will cause performance issues due to unnecessary API calls.Apply this diff to fix the server-side session retrieval:
-import { getSession } from 'next-auth/react' +import { getServerSession } from 'next-auth/next' +import { authOptions } from '../app/api/auth/[...nextauth]/route' -const session = await getSession() +const session = await getServerSession(authOptions)Note: You may need to adjust the authOptions import path based on your NextAuth configuration location.
20-21
: Authorization header implementation looks correct.The Bearer token authorization setup is properly implemented with conditional header inclusion and appropriate eslint disable comment for the naming convention.
frontend/src/components/Card.tsx (1)
79-86
: LGTM! Timeline section is well-implemented.The timeline display correctly:
- Validates both dates exist before rendering
- Uses locale-aware date formatting
- Has appropriate styling for light/dark modes
frontend/src/types/card.ts (1)
26-29
: Type definitions look good.The new properties added to CardProps and DetailsCardProps are well-structured:
- Timeline dates support both string and Date types for flexibility
- All new DetailsCardProps fields are appropriately optional
- Types are properly imported from their respective modules
Also applies to: 44-69
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
♻️ Duplicate comments (5)
backend/apps/mentorship/graphql/nodes/mentor.py (1)
7-10
: Add type annotation for github_user attribute.The
MentorNode
class accessesself.github_user
in field methods but doesn't declare this attribute with proper typing, which could lead to IDE warnings and reduced type safety.Consider adding the missing type annotation:
+from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from apps.github.models import User as GithubUser + @strawberry.type class MentorNode: """A GraphQL node representing a mentorship mentor.""" id: strawberry.ID + github_user: "GithubUser"backend/apps/mentorship/utils/user.py (2)
25-27
: Restrict lookup to login only to avoid false positives.The current query uses both login and name for matching, which can cause false positives since display names are not unique and may change.
- try: - github_user = GithubUser.objects.get(models.Q(login__iexact=login.lower())) - except GithubUser.DoesNotExist as err: - raise Exception("No GithubUser found matching this login or name") from err + try: + github_user = GithubUser.objects.get(login__iexact=login) + except GithubUser.DoesNotExist as err: + raise Exception("No GithubUser found matching this login") from err
17-22
: Improve exception handling specificity.The broad exception handling masks specific GitHub API errors that could be handled more appropriately.
try: github = Github(access_token) gh_user = github.get_user() login = gh_user.login - except Exception as err: - raise Exception("GitHub token is invalid or expired") from err + except github.BadCredentialsException as err: + raise Exception("Invalid GitHub credentials") from err + except github.RateLimitExceededException as err: + raise Exception("GitHub API rate limit exceeded") from err + except Exception as err: + raise Exception("GitHub API error occurred") from errfrontend/src/app/mentorship/programs/create/page.tsx (1)
49-97
: Add form validation before submission.The form submission lacks validation for required fields, which could result in unnecessary GraphQL calls and poor user experience.
const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() + // Validate required fields + if (!formData.name.trim()) { + addToast({ + title: 'Validation Error', + description: 'Program name is required.', + color: 'danger', + variant: 'solid', + timeout: 3000, + shouldShowTimeoutProgress: true, + }) + return + } + + if (!formData.description.trim()) { + addToast({ + title: 'Validation Error', + description: 'Program description is required.', + color: 'danger', + variant: 'solid', + timeout: 3000, + shouldShowTimeoutProgress: true, + }) + return + } try { const input = { // ... existing code }backend/apps/mentorship/graphql/mutations/module.py (1)
66-70
: Fix inconsistent GitHub user lookup in create_module.The GitHub user lookup uses case-sensitive matching while the
update_module
method uses case-insensitive matching. This inconsistency could cause issues when different case variations of the same username are used.- try: - github_user = GithubUser.objects.get(login=login) - except GithubUser.DoesNotExist as err: - raise Exception("GitHub username not found.") from err + try: + github_user = GithubUser.objects.get(login__iexact=login.lower()) + except GithubUser.DoesNotExist as err: + raise Exception("GitHub username not found.") from err
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (11)
backend/apps/mentorship/graphql/mutations/module.py
(1 hunks)backend/apps/mentorship/graphql/nodes/mentor.py
(1 hunks)backend/apps/mentorship/graphql/queries/module.py
(1 hunks)backend/apps/mentorship/utils/user.py
(1 hunks)backend/apps/nest/graphql/mutations/user.py
(2 hunks)frontend/src/app/api/auth/[...nextauth]/route.ts
(4 hunks)frontend/src/app/mentorship/programs/create/page.tsx
(1 hunks)frontend/src/components/ModuleCard.tsx
(1 hunks)frontend/src/server/apolloClient.ts
(1 hunks)frontend/src/types/card.ts
(3 hunks)frontend/src/utils/dateFormatter.ts
(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
frontend/src/components/ModuleCard.tsx (3)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
Learnt from: codic-yeeshu
PR: OWASP/Nest#1444
File: frontend/src/components/NavDropDown.tsx:0-0
Timestamp: 2025-04-30T13:41:20.846Z
Learning: Use React's useId() hook rather than manually generating random strings when creating accessibility identifiers for UI components. This creates stable, unique IDs without causing hydration mismatches.
frontend/src/types/card.ts (2)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
Learnt from: Rajgupta36
PR: OWASP/Nest#1634
File: backend/apps/nest/graphql/mutations/user.py:24-48
Timestamp: 2025-06-18T21:00:33.024Z
Learning: GitHub access tokens use specific prefixes to identify token types: OAuth tokens start with `gho_`, classic personal access tokens with `ghp_`, fine-grained personal access tokens with `github_pat_`, GitHub App user tokens with `ghu_`, GitHub App installation tokens with `ghs_`, and refresh tokens with `ghr_`. This is documented in GitHub's official authentication documentation and engineering blog.
🧬 Code Graph Analysis (4)
frontend/src/components/ModuleCard.tsx (5)
frontend/src/types/program.ts (1)
Module
(32-44)frontend/src/components/TruncatedText.tsx (1)
TruncatedText
(3-45)frontend/src/components/InfoItem.tsx (1)
TextInfoItem
(33-48)frontend/src/utils/capitalize.ts (1)
capitalize
(1-4)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/types/card.ts (9)
frontend/src/types/chapter.ts (1)
Chapter
(3-19)frontend/src/types/healthMetrics.ts (1)
HealthMetricsProps
(1-14)frontend/src/types/contributor.ts (1)
Contributor
(1-8)frontend/src/types/pullRequest.ts (1)
PullRequest
(3-10)frontend/src/types/issue.ts (1)
Issue
(3-19)frontend/src/types/milestone.ts (1)
Milestone
(3-15)frontend/src/types/release.ts (1)
Release
(3-14)frontend/src/types/project.ts (1)
RepositoryCardProps
(51-61)frontend/src/types/program.ts (1)
Module
(32-44)
backend/apps/nest/graphql/mutations/user.py (2)
backend/apps/nest/models/user.py (1)
User
(9-47)backend/apps/github/models/user.py (1)
User
(14-134)
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
frontend/src/server/apolloClient.ts (1)
apolloClient
(43-44)
🪛 Biome (1.9.4)
frontend/src/components/ModuleCard.tsx
[error] 31-34: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form
element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
[error] 59-62: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form
element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🔇 Additional comments (10)
frontend/src/utils/dateFormatter.ts (1)
63-70
: Implementation correctly handles invalid dates.The function properly validates date inputs and throws an error for invalid dates, following the same pattern as existing functions in the file. The past review comment concern has been addressed.
frontend/src/types/card.ts (2)
11-11
: Import statement syntax is correct.The import statement uses proper TypeScript type-only import syntax and is correctly formatted. The past review comment concern has been resolved.
26-29
: Well-structured type extensions for mentorship features.The new optional properties added to
CardProps
andDetailsCardProps
properly support the mentorship program functionality. The types are consistent with the existing patterns and integrate well with the imported types.Also applies to: 44-45, 50-51, 53-55, 61-62, 69-69
frontend/src/components/ModuleCard.tsx (1)
25-27
: Key usage is correctly implemented.The component properly uses unique identifiers (
module.key || module.id
) as React keys instead of array indices, which addresses the past review comment concern and ensures stable rendering.backend/apps/mentorship/graphql/nodes/mentor.py (1)
15-31
: LGTM - Proper defensive programming implemented.The field methods now correctly handle cases where
github_user
might be None, preventing potential AttributeError exceptions. The implementation returns appropriate empty strings as fallbacks.backend/apps/nest/graphql/mutations/user.py (2)
47-53
: Excellent performance optimization with database queries.The role determination logic now uses efficient database queries instead of nested loops, significantly improving performance for large datasets. The
Q
object usage for OR conditions is appropriate for checking both login and name fields.
64-66
: Good role synchronization logic.The code properly handles role updates for existing users, ensuring that changes in project leadership are reflected in user roles without unnecessary database writes.
frontend/src/app/mentorship/programs/create/page.tsx (1)
32-47
: LGTM - Proper access control with early return.The access control logic correctly implements early returns to prevent unauthorized users from accessing the create program functionality. The loading state check and role verification are properly implemented.
frontend/src/app/api/auth/[...nextauth]/route.ts (1)
24-33
: Well-structured GraphQL mutation constant.The extracted
GITHUB_AUTH_MUTATION
constant improves code reusability and maintainability by eliminating duplication between thesignIn
andjwt
callbacks.backend/apps/mentorship/graphql/queries/module.py (1)
22-26
: Excellent query optimization.The use of
select_related("project")
andprefetch_related("mentors__github_user")
demonstrates good understanding of Django ORM optimization to avoid N+1 query problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
frontend/src/app/mentorship/programs/[programKey]/page.tsx (1)
47-56
: Consider adding runtime type validation for session data.The admin check relies on the username from session data, but the type casting may not guarantee the username exists at runtime.
- const username = (session as SessionWithRole)?.user?.username + const username = (session as SessionWithRole)?.user?.username || null const isAdmin = useMemo( - () => !!program?.admins?.some((admin) => admin.login === username), + () => !!username && !!program?.admins?.some((admin) => admin.login === username), [program, username] )
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
frontend/src/app/mentorship/programs/[programKey]/page.tsx
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run frontend unit tests
- GitHub Check: Run backend tests
- GitHub Check: Run frontend e2e tests
- GitHub Check: CodeQL (javascript-typescript)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make it CI/CD green before sending for review?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
backend/apps/mentorship/api/internal/queries/program.py (2)
39-66
: Guard limit to prevent ZeroDivisionError and runaway paginationlimit can be 0 or negative, causing ZeroDivisionError and/or huge slices. Clamp both page and limit.
def my_programs( self, info: strawberry.Info, search: str = "", page: int = 1, - limit: int = 24, + limit: int = 24, ) -> PaginatedPrograms: """Get paginated programs where the current user is admin or mentor.""" user = info.context.request.user + # Normalize pagination params + if page is None or page < 1: + page = 1 + # Reasonable upper bound to protect DB; consider moving to settings + if limit is None or limit < 1: + limit = 24 + elif limit > 100: + limit = 100 + try: mentor = Mentor.objects.select_related("github_user").get(github_user=user.github_user) except Mentor.DoesNotExist: logger.warning("Mentor for user '%s' not found.", user.username) return PaginatedPrograms(programs=[], total_pages=0, current_page=page) queryset = (
14-15
: Unify and externalize page size; avoid conflicting defaults (25 vs 24)PAGE_SIZE is unused and disagrees with the default limit. Load from settings and use one source of truth.
-import logging +import logging +from django.conf import settings @@ -PAGE_SIZE = 25 +PAGE_SIZE = getattr(settings, "MENTORSHIP_PAGE_SIZE", 24) @@ - page: int = 1, - limit: int = 24, + page: int = 1, + limit: int = PAGE_SIZE,Also applies to: 40-41
backend/apps/mentorship/api/internal/mutations/module.py (2)
95-115
: Ensure program re-indexing is triggered after experience_levels changeProgram experience_levels are mutated; confirm that program.save(update_fields=["experience_levels"]) triggers the post_save signal that invalidates/updates the "programs" index.
If signals aren’t connected for Program in this app init path, explicitly enqueue reindex after save. To verify:
#!/bin/bash rg -n "post_save.*Program" -A 3 rg -n "Algolia|index.*programs" -A 3 rg -n "signals.*program" -A 2Expected: a post_save receiver for Program that clears Algolia cache or schedules re-index.
Also applies to: 178-196
81-84
: Return generic 403; log details instead of including them in API errorsAvoid informative auth error messages. Log context, raise bare PermissionDenied.
- except Mentor.DoesNotExist as e: - msg = "Only mentors can create modules." - raise PermissionDenied(msg) from e + except Mentor.DoesNotExist as e: + logger.warning("User '%s' is not a mentor and cannot create modules.", user.username, exc_info=True) + raise PermissionDenied from e @@ - except Mentor.DoesNotExist as err: - msg = "Only mentors can edit modules." - logger.warning( - "User '%s' is not a mentor and cannot edit modules.", - user.username, - exc_info=True, - ) - raise PermissionDenied(msg) from err + except Mentor.DoesNotExist as err: + logger.warning("User '%s' is not a mentor and cannot edit modules.", user.username, exc_info=True) + raise PermissionDenied from errAlso applies to: 131-141
🧹 Nitpick comments (6)
backend/apps/core/api/internal/algolia.py (2)
51-58
: Clamp hitsPerPage/page and ensure indexName presence to prevent expensive requestsConfirm validate_search_params enforces:
- indexName is a non-empty string,
- page >= 1,
- hitsPerPage within a sane max (e.g., <= 100).
If not, add a cheap server-side clamp before using these values to avoid pathological requests and cache amplification.
Example (illustrative, not a diff):
max_hits = getattr(settings, "SEARCH_MAX_HITS_PER_PAGE", 100) limit = max(1, min(int(data.get("hitsPerPage", 25)), max_hits)) page = max(1, int(data.get("page", 1)))
34-38
: Prefer HttpResponseNotAllowed for 405For method checks, return HttpResponseNotAllowed(["POST"]) instead of a JsonResponse with 405 for clearer semantics.
backend/apps/mentorship/index/registry/program.py (2)
39-44
: Enable exact lookup by key in searchConsider making key searchable to support quick lookups by program key.
"searchableAttributes": [ "unordered(idx_description)", "unordered(idx_experience_levels)", "unordered(idx_name)", + "idx_key", ],
29-38
: Ranking includes 'custom' but customRanking is emptyEither remove 'custom' from ranking or add a meaningful customRanking to avoid confusing behavior.
- "customRanking": [], - "ranking": [ + "customRanking": [], + "ranking": [ "typo", "words", "filters", "proximity", "attribute", "exact", - "custom", + # "custom", # enable if customRanking is set ],backend/apps/mentorship/api/internal/mutations/module.py (2)
26-35
: Minor: avoid redundant lower() when using iexactiexact already handles case-insensitive match.
- github_user = GithubUser.objects.get(login__iexact=login.lower()) + github_user = GithubUser.objects.get(login__iexact=login)
74-86
: Optional: lock Program row when changing experience_levels to avoid racesTwo concurrent module creates/updates could clobber ArrayField/List updates. Consider select_for_update on Program within the atomic transaction.
- program = Program.objects.get(key=input_data.program_key) + program = Program.objects.select_for_update().get(key=input_data.program_key) @@ - if not module.program.admins.filter(id=creator_as_mentor.id).exists(): + if not module.program.admins.filter(id=creator_as_mentor.id).exists(): raise PermissionDenied @@ - module.program.save(update_fields=["experience_levels"]) + module.program.save(update_fields=["experience_levels"])For update_module, if stronger guarantees are needed:
# Before mutating experience_levels Program.objects.select_for_update().filter(pk=module.program_id).exists()Also applies to: 119-127, 142-150, 180-196
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
backend/apps/core/api/internal/algolia.py
(1 hunks)backend/apps/core/constants.py
(1 hunks)backend/apps/core/utils/index.py
(3 hunks)backend/apps/mentorship/api/internal/mutations/__init__.py
(1 hunks)backend/apps/mentorship/api/internal/mutations/module.py
(1 hunks)backend/apps/mentorship/api/internal/mutations/program.py
(1 hunks)backend/apps/mentorship/api/internal/queries/module.py
(1 hunks)backend/apps/mentorship/api/internal/queries/program.py
(1 hunks)backend/apps/mentorship/index/registry/program.py
(1 hunks)backend/apps/mentorship/models/mixins/program.py
(1 hunks)backend/apps/mentorship/signals/program.py
(1 hunks)backend/pyproject.toml
(1 hunks)backend/settings/graphql.py
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- backend/apps/core/constants.py
🚧 Files skipped from review as they are similar to previous changes (8)
- backend/pyproject.toml
- backend/apps/mentorship/api/internal/mutations/init.py
- backend/apps/mentorship/signals/program.py
- backend/apps/core/utils/index.py
- backend/settings/graphql.py
- backend/apps/mentorship/api/internal/queries/module.py
- backend/apps/mentorship/models/mixins/program.py
- backend/apps/mentorship/api/internal/mutations/program.py
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:166-166
Timestamp: 2025-07-13T05:55:46.436Z
Learning: In the OWASP Nest mentorship GraphQL mutations, Strawberry GraphQL automatically converts between Django Program instances and ProgramNode types, so mutations can return Program instances directly without manual conversion even when typed as ProgramNode, similar to the Module/ModuleNode pattern.
📚 Learning: 2025-07-13T05:55:46.436Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:166-166
Timestamp: 2025-07-13T05:55:46.436Z
Learning: In the OWASP Nest mentorship GraphQL mutations, Strawberry GraphQL automatically converts between Django Program instances and ProgramNode types, so mutations can return Program instances directly without manual conversion even when typed as ProgramNode, similar to the Module/ModuleNode pattern.
Applied to files:
backend/apps/mentorship/api/internal/queries/program.py
backend/apps/mentorship/api/internal/mutations/module.py
📚 Learning: 2025-07-11T15:57:56.648Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/queries/module.py:39-50
Timestamp: 2025-07-11T15:57:56.648Z
Learning: In the OWASP Nest mentorship GraphQL queries, Strawberry GraphQL automatically converts between Django Module instances and ModuleNode types, so methods can return Module instances directly without manual conversion even when typed as ModuleNode.
Applied to files:
backend/apps/mentorship/api/internal/queries/program.py
backend/apps/mentorship/api/internal/mutations/module.py
📚 Learning: 2025-07-14T16:18:07.287Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleForm.tsx:112-134
Timestamp: 2025-07-14T16:18:07.287Z
Learning: In the OWASP Nest mentorship system, date validation for modules (ensuring start date precedes end date) is handled on the backend in the module GraphQL mutations via the `_validate_module_dates` helper function in backend/apps/mentorship/graphql/mutations/module.py, which prevents invalid date ranges from being stored in the database.
Applied to files:
backend/apps/mentorship/api/internal/queries/program.py
backend/apps/mentorship/api/internal/mutations/module.py
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.
Applied to files:
backend/apps/mentorship/api/internal/queries/program.py
📚 Learning: 2025-07-31T07:05:25.056Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#0
File: :0-0
Timestamp: 2025-07-31T07:05:25.056Z
Learning: In the OWASP Nest project, Django views may not properly access authenticated users from sessions created by Strawberry GraphQL mutations. The issue occurs because Django's AuthenticationMiddleware doesn't always populate request.user from session data that GraphQL context successfully uses via info.context.request.user. The solution is to manually resolve users from session data using request.session.get('_auth_user_id') and User.objects.select_related('github_user').get(pk=user_id) to match the same authentication mechanism used by GraphQL context.
Applied to files:
backend/apps/mentorship/api/internal/queries/program.py
🔇 Additional comments (3)
backend/apps/core/api/internal/algolia.py (1)
17-17
: CACHE_PREFIX import verified; validators in place; recommend minor nit
- The constant
CACHE_PREFIX
is defined as"algolia_proxy"
inbackend/apps/core/constants.py
, matching the previous literal.clear_index_cache
inbackend/apps/core/utils/index.py
uses the sameCACHE_PREFIX
for its key pattern (f"{CACHE_PREFIX}:{index_name}*"
), so cache-clear tooling remains aligned.validate_search_params
(inbackend/apps/core/validators.py
) callsvalidate_limit
andvalidate_page
, ensuringhitsPerPage
andpage
are within allowed bounds and will raiseValidationError
on invalid input. It also validatesindexName
is non-empty viavalidate_index_name
.Nit: For 405 responses, prefer returning
HttpResponseNotAllowed([...])
instead of a genericJsonResponse
.No further action needed on imports or validation logic.
backend/apps/mentorship/index/registry/program.py (1)
23-44
: No change needed: retainindexLanguages
Algolia’s API usesindexLanguages
to configure language-specific processing at index time, whilequeryLanguages
applies only at query time (stop words, plurals, etc.). All existing registries correctly useindexLanguages
, so you can ignore the suggestion to switch toqueryLanguages
.Likely an incorrect or invalid review comment.
backend/apps/mentorship/api/internal/queries/program.py (1)
22-33
: Restrict visibility of unpublished programs (if intended)get_program is public. If unpublished/draft programs should not be visible, filter by published status or add permission checks.
- try: - program = Program.objects.prefetch_related("admins__github_user").get(key=program_key) + try: + program = ( + Program.objects.prefetch_related("admins__github_user") + .get( + key=program_key, + status=Program.ProgramStatus.PUBLISHED, # remove if drafts should be visible + ) + )If drafts must be visible to admins only:
- Keep get by key, then:
if program.status != Program.ProgramStatus.PUBLISHED: # Optionally enforce IsAuthenticated and admin check here raise ObjectDoesNotExist("Program with key not found.")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
frontend/src/hooks/useDjangoSession.ts (2)
6-6
: Resolved: redundant local ExtendedSession type removedGood move to source ExtendedSession from central types.
61-71
: Resolved: no longer throwing inside effect catchThanks for switching to toasts instead of throwing; avoids crashes.
🧹 Nitpick comments (1)
frontend/src/hooks/useDjangoSession.ts (1)
81-81
: Nit: use property shorthandMinor cleanup.
- status: status, + status,
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
frontend/__tests__/unit/components/UserMenu.test.tsx
(34 hunks)frontend/src/app/api/auth/[...nextauth]/route.ts
(4 hunks)frontend/src/components/UserMenu.tsx
(5 hunks)frontend/src/hooks/useDjangoSession.ts
(2 hunks)frontend/src/server/queries/authQueries.ts
(1 hunks)frontend/src/types/auth.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- frontend/src/server/queries/authQueries.ts
- frontend/tests/unit/components/UserMenu.test.tsx
- frontend/src/app/api/auth/[...nextauth]/route.ts
- frontend/src/types/auth.ts
- frontend/src/components/UserMenu.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:166-166
Timestamp: 2025-07-13T05:55:46.436Z
Learning: In the OWASP Nest mentorship GraphQL mutations, Strawberry GraphQL automatically converts between Django Program instances and ProgramNode types, so mutations can return Program instances directly without manual conversion even when typed as ProgramNode, similar to the Module/ModuleNode pattern.
🧬 Code Graph Analysis (1)
frontend/src/hooks/useDjangoSession.ts (1)
frontend/src/types/auth.ts (1)
ExtendedSession
(8-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run backend tests
- GitHub Check: Run frontend unit tests
- GitHub Check: Run frontend e2e tests
- GitHub Check: CodeQL (javascript-typescript)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (2)
frontend/src/app/api/auth/[...nextauth]/route.ts (2)
48-56
: Add defensive checks in profile callback.The profile callback should include defensive checks as mentioned in past review comments to handle cases where GitHub's API response structure changes.
profile(profile) { return { - email: profile.email, - id: profile.id.toString(), - image: profile.avatar_url, - login: profile.login, - name: profile.name, + email: profile.email ?? '', + id: profile.id?.toString() ?? '', + image: profile.avatar_url ?? '', + login: profile.login ?? '', + name: profile.name ?? profile.login ?? '', } },
27-39
: Apply same improvements to checkIfMentor function.This function has identical issues to
checkIfProjectLeader
and should receive the same fixes.async function checkIfMentor(login: string): Promise<boolean> { + // Input validation + if (!login || typeof login !== 'string' || !login.trim()) { + return false + } + try { const client = await apolloClient const { data } = await client.query({ query: IS_MENTOR_QUERY, - variables: { login }, + variables: { login: login.trim() }, fetchPolicy: 'no-cache', }) return data?.isMentor ?? false } catch (err) { - throw new Error('Failed to fetch mentor status Error', err) + console.error('Failed to fetch mentor status:', err) + return false // Don't throw to avoid breaking auth flow } }
🧹 Nitpick comments (1)
backend/apps/mentorship/api/internal/queries/mentorship.py (1)
9-14
: Remove unused UserRolesResult type.The
UserRolesResult
type is defined but never used in this file. Consider removing it to reduce code clutter.-@strawberry.type -class UserRolesResult: - """Result type for user roles query.""" - - roles: list[str] - -
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
backend/apps/mentorship/api/internal/queries/mentorship.py
(1 hunks)frontend/__tests__/unit/pages/CreateModule.test.tsx
(1 hunks)frontend/src/app/api/auth/[...nextauth]/route.ts
(4 hunks)frontend/src/server/queries/mentorshipQueries.ts
(1 hunks)frontend/src/types/auth.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/tests/unit/pages/CreateModule.test.tsx
- frontend/src/types/auth.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.
Applied to files:
frontend/src/app/api/auth/[...nextauth]/route.ts
🧬 Code Graph Analysis (2)
backend/apps/mentorship/api/internal/queries/mentorship.py (2)
backend/apps/github/api/internal/queries/user.py (1)
user
(40-56)backend/apps/mentorship/models/mentor.py (1)
Mentor
(11-40)
frontend/src/app/api/auth/[...nextauth]/route.ts (3)
frontend/src/server/apolloClient.ts (1)
apolloClient
(36-37)frontend/src/server/queries/mentorshipQueries.ts (2)
IS_PROJECT_LEADER_QUERY
(3-7)IS_MENTOR_QUERY
(9-13)frontend/src/types/auth.ts (2)
ExtendedProfile
(3-6)ExtendedSession
(8-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run frontend e2e tests
- GitHub Check: Run frontend unit tests
- GitHub Check: Run backend tests
- GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (1)
frontend/src/server/queries/mentorshipQueries.ts (1)
1-13
: File structure is appropriate for GraphQL queries.Contrary to the past review comment, this file correctly contains GraphQL queries (not mutations) and is appropriately placed in the
queries
directory. TheIS_PROJECT_LEADER_QUERY
andIS_MENTOR_QUERY
are legitimate query operations that retrieve boolean values based on login parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (5)
frontend/src/app/mentorship/programs/[programKey]/page.tsx (3)
29-33
: Remove duplicated state and derive from Apollo data to prevent stale UI.Duplicating Apollo data in local state can cause 404 flicker and stale data issues. Derive directly from query data instead.
- const [program, setProgram] = useState<Program | null>(null) - const [modules, setModules] = useState<Module[]>([]) - const [isRefetching, setIsRefetching] = useState(false) - - const isLoading = isQueryLoading || isRefetching + const program = data?.getProgram as Program | undefined + const modules = (data?.getProgramModules ?? []) as Module[] + const isLoading = isQueryLoading
44-48
: Use usePathname instead of window.location.pathname.Using window.location.pathname is not ideal in Next.js app router.
+ import { useParams, useSearchParams, useRouter, usePathname } from 'next/navigation' ... + const pathname = usePathname() ... - router.replace(cleaned ? `?${cleaned}` : window.location.pathname, { scroll: false }) + router.replace(cleaned ? `?${cleaned}` : pathname, { scroll: false })
19-27
: Add explicit error handling for GraphQL query.Missing error handling can cause silent failures.
const { data, refetch, loading: isQueryLoading, + error, } = useQuery(GET_PROGRAM_AND_MODULES, { variables: { programKey }, skip: !programKey, notifyOnNetworkStatusChange: true, })
Then add error display after loading check:
if (isLoading) return <LoadingSpinner /> + + if (error) { + return ( + <ErrorDisplay + statusCode={500} + title="Failed to load program" + message={error.message} + /> + ) + }frontend/src/components/ModuleCard.tsx (1)
15-15
: Move getSimpleDuration to a shared utility to eliminate circular dependency.ModuleCard imports SingleModuleCard, and SingleModuleCard imports getSimpleDuration from ModuleCard, creating a circular dependency.
Create a new utility file
frontend/src/utils/duration.ts
:export const getSimpleDuration = (start: string, end: string): string => { const startDate = new Date(start) const endDate = new Date(end) if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { return 'Invalid duration' } const ms = endDate.getTime() - startDate.getTime() const days = Math.floor(ms / (1000 * 60 * 60 * 24)) const weeks = Math.ceil(days / 7) return `${weeks} week${weeks !== 1 ? 's' : ''}` }Then update both files to import from the utility:
+ import { getSimpleDuration } from 'utils/duration'
And remove the local export from ModuleCard.
Also applies to: 98-110
frontend/src/components/CardDetailsPage.tsx (1)
319-319
: Add TypeScript types for SocialLinks component props.Missing prop types reduce type safety.
+ interface SocialLinksProps { + urls: string[] + } + - export const SocialLinks = ({ urls }) => { + export const SocialLinks = ({ urls }: SocialLinksProps) => {
🧹 Nitpick comments (2)
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (1)
17-19
: Consider using more specific state types.The admins state uses
any
type which reduces type safety.- const [admins, setAdmins] = useState(null) + const [admins, setAdmins] = useState<Array<{ login: string; name?: string; avatarUrl?: string }> | null>(null)frontend/src/components/CardDetailsPage.tsx (1)
85-87
: Simplify admin check with optional chaining.The type casting and complex chaining can be simplified.
- admins?.some( - (admin) => admin.login === ((data as ExtendedSession)?.user?.login as string) - ) + admins?.some( + (admin) => admin.login === (data as ExtendedSession)?.user?.login + )
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
backend/pyproject.toml
(1 hunks)frontend/__tests__/unit/components/CardDetailsPage.test.tsx
(1 hunks)frontend/__tests__/unit/components/SingleModuleCard.test.tsx
(1 hunks)frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
(1 hunks)frontend/src/app/mentorship/programs/[programKey]/page.tsx
(1 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
(1 hunks)frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
(1 hunks)frontend/src/components/CardDetailsPage.tsx
(8 hunks)frontend/src/components/ModuleCard.tsx
(1 hunks)frontend/src/components/SingleModuleCard.tsx
(1 hunks)frontend/src/utils/capitalize.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
- backend/pyproject.toml
- frontend/src/utils/capitalize.ts
- frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
- frontend/tests/unit/components/CardDetailsPage.test.tsx
- frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
- frontend/src/components/SingleModuleCard.tsx
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/components/CardDetailsPage.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/components/ModuleCard.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/components/ModuleCard.tsx
📚 Learning: 2025-07-12T17:14:28.536Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/[programKey]/edit/page.tsx:90-128
Timestamp: 2025-07-12T17:14:28.536Z
Learning: Both ProgramForm (programCard.tsx) and ModuleForm (mainmoduleCard.tsx) components already implement HTML validation using the `required` attribute on form fields. The browser's native validation prevents form submission and displays error messages when required fields are empty, eliminating the need for additional JavaScript validation before GraphQL mutations.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-12T17:12:25.807Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/__tests__/unit/pages/ProgramDetails.test.tsx:35-0
Timestamp: 2025-07-12T17:12:25.807Z
Learning: In React applications, button elements should always have an explicit type attribute (type="button", type="submit", or type="reset") to prevent unintended form submission behavior, even when the button appears to be outside of a form context. The default type is "submit" which can cause unexpected behavior.
Applied to files:
frontend/src/components/ModuleCard.tsx
frontend/src/components/CardDetailsPage.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/src/components/ModuleCard.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/src/components/ModuleCard.tsx
frontend/src/components/CardDetailsPage.tsx
🧬 Code Graph Analysis (5)
frontend/src/app/mentorship/programs/[programKey]/page.tsx (6)
frontend/src/server/queries/programsQueries.ts (1)
GET_PROGRAM_AND_MODULES
(61-96)frontend/src/types/mentorship.ts (2)
Program
(22-37)Module
(52-64)backend/apps/mentorship/models/program.py (1)
Program
(18-87)backend/apps/mentorship/models/module.py (1)
Module
(16-88)frontend/src/app/global-error.tsx (1)
ErrorDisplay
(28-51)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/components/ModuleCard.tsx (6)
frontend/src/types/mentorship.ts (1)
Module
(52-64)backend/apps/mentorship/models/module.py (1)
Module
(16-88)backend/apps/mentorship/api/internal/nodes/program.py (1)
admins
(32-34)frontend/src/components/TruncatedText.tsx (1)
TruncatedText
(3-45)frontend/src/components/InfoItem.tsx (1)
TextInfoItem
(37-52)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (6)
frontend/src/types/mentorship.ts (1)
Module
(52-64)backend/apps/mentorship/api/internal/nodes/program.py (1)
admins
(32-34)frontend/src/server/queries/moduleQueries.ts (1)
GET_PROGRAM_ADMINS_AND_MODULES
(48-77)frontend/src/app/global-error.tsx (2)
handleAppError
(66-86)ErrorDisplay
(28-51)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)frontend/src/components/ModuleCard.tsx (1)
getSimpleDuration
(98-110)
frontend/src/components/CardDetailsPage.tsx (4)
backend/apps/github/models/common.py (1)
title
(40-44)backend/apps/mentorship/api/internal/nodes/program.py (1)
admins
(32-34)frontend/src/types/auth.ts (1)
ExtendedSession
(8-20)backend/apps/mentorship/api/internal/nodes/module.py (1)
mentors
(29-31)
frontend/__tests__/unit/components/SingleModuleCard.test.tsx (2)
frontend/src/types/auth.ts (1)
ExtendedSession
(8-20)frontend/src/wrappers/testUtil.tsx (1)
render
(14-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run frontend e2e tests
- GitHub Check: Run backend tests
- GitHub Check: Run frontend unit tests
- GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (1)
frontend/__tests__/unit/components/SingleModuleCard.test.tsx (1)
1-414
: Well-structured test suite with comprehensive coverage!The test file demonstrates excellent testing practices with proper mocking, edge case handling, and accessibility testing.
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is ready to be merged in from the frontend perspective 👍🏼
We can always update it later if needed. I also pushed a tiny change for module description styling.
Well done!
|
* program mutation and node * UI for program create , view and edit * backend completed * create module edit and view page * type fix * update components * updated backend * slugify urls * pre commit * fix components * update suggestions * update pnpm-lock file * fix spell check * fix publish program * fix unit test cases * fix pre-commits * update frontend logic * format * refactor code * fix terminal * suggestion fixes * added unit test cases * fix test cases * optimization * generated code * update code * update code * update naming,validation logic * cleanup * fix project suggestion bug * update code * fixes * apply suggestions * update suggestion * update code * update code * update frozen-lockfile * update code and make program page view only * updated migration and add query * added index * updated components, mutaitons and nodes * update code * added constraints * fix test case * added unit test case for program page * added remaining unit test cases * fix bug * update code * updated auth and projectleader code * update test cases * update code * revert test case * fix unit test cases * migration fix * increase test coverage * update suggestions * added function for cache clear by index_name * created component for single module * fix typo * fix some frontend test cases * added post save signal * added more test cases * pnpm updated * increase coverage * update suggestion * updated backend suggestions * added isMentor query * fix page loading * Update module description styling * Update code --------- Co-authored-by: Kate Golovanova <[email protected]> Co-authored-by: Arkadii Yakovets <[email protected]>
Resolve(#1716)
Tasks -
Media
I have fixed publish program logic
https://github.com/user-attachments/assets/ac1cac1b-9b34-4921-b691-dbc652e3683e