Skip to content

Conversation

Rajgupta36
Copy link
Collaborator

@Rajgupta36 Rajgupta36 commented Jul 6, 2025

Resolve(#1716)


Tasks -

  • Created module view ,edit and add page
  • create program view , edit page with pagination and search functionality
  • Added mutation related to program,module
  • Added nodes related to program ,module
  • Added queries related to program, module
  • Implemented program-module relationship queries
  • Updated models

Media

I have fixed publish program logic
https://github.com/user-attachments/assets/ac1cac1b-9b34-4921-b691-dbc652e3683e

@Rajgupta36 Rajgupta36 requested review from arkid15r and kasya as code owners July 6, 2025 21:36
Copy link
Contributor

coderabbitai bot commented Jul 6, 2025

Summary by CodeRabbit

  • New Features
    • Mentorship hub: browse searchable programs, view program and module detail pages.
    • “My Mentorship” dashboard for project leaders: create/edit programs and add/edit modules.
    • Role‑based UI: admin actions (publish/draft/complete), edit buttons, and protected /my/mentorship routes.
    • Program/module timelines, duration, tags, domains, mentors, and admins displayed.
  • UI Improvements
    • Enhanced details cards, module lists with show more/less, and project autocomplete in forms.
    • User menu now shows “My Mentorship” when eligible.
  • Authentication & Access
    • Session now carries leader/mentor flags to gate features.
  • Tests
    • Extensive unit tests covering pages, components, and flows.

Walkthrough

Introduces 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

Cohort / File(s) Summary
Backend: Mentorship models, migrations, indexing, signals
backend/apps/mentorship/models/module.py, .../models/program.py, .../migrations/0004_module_key_program_key_and_more.py, .../models/mixins/*, .../index/__init__.py, .../index/registry/program.py, .../signals/*, .../apps.py, .../admin/program.py
Adds slugged key fields (unique per program/module), unique constraints, ProgramIndexMixin, Algolia ProgramIndex, post_save signal to clear Algolia cache, app ready hook, and minor admin import refactor.
Backend: Mentorship GraphQL API
backend/apps/mentorship/api/internal/mutations/*, .../nodes/*, .../queries/*
New Strawberry schema for mentorship: enums, nodes (Program, Module, Mentor), queries (program/module fetch, role checks, pagination), and mutations for creating/updating programs/modules and program status.
Backend: GraphQL schema wiring
backend/settings/graphql.py
Extends Query/Mutation to include mentorship program and module queries/mutations.
Backend: Core utils/constants and OWASP API
backend/apps/core/utils/index.py, .../constants.py, .../core/api/internal/algolia.py, backend/apps/owasp/api/internal/queries/project.py, .../nodes/project.py, backend/apps/owasp/models/__init__.py
Adds Algolia cache clearing and programs index params; centralizes CACHE_PREFIX; OWASP ProjectQuery gains searchProjects and isProjectLeader; ProjectNode exposes id; exports Project at package.
Frontend: Auth/session/middleware
frontend/src/app/api/auth/[...nextauth]/route.ts, .../hooks/useDjangoSession.ts, .../types/auth.ts, .../middleware.ts, .../components/UserMenu.tsx, frontend/jest.setup.ts
Adds role checks (isProjectLeader/isMentor) into JWT/session, exposes status in hook, extends auth types, protects /my/mentorship routes, updates menu conditional links by roles, and test mock for useSession.
Frontend: Public mentorship pages
frontend/src/app/mentorship/programs/page.tsx, .../programs/[programKey]/page.tsx, .../programs/[programKey]/modules/[moduleKey]/page.tsx
New pages to list programs (Algolia), show program details with modules, and module details; loading/error handling included.
Frontend: My Mentorship (admin) pages
frontend/src/app/my/mentorship/page.tsx, .../programs/create/page.tsx, .../programs/[programKey]/page.tsx, .../programs/[programKey]/edit/page.tsx, .../programs/[programKey]/modules/create/page.tsx, .../programs/[programKey]/modules/[moduleKey]/page.tsx, .../programs/[programKey]/modules/[moduleKey]/edit/page.tsx
Adds admin dashboard, create/edit/detail pages for programs and modules with access control, mutations, toasts, and refetch logic.
Frontend: Forms
frontend/src/components/ProgramForm.tsx, frontend/src/components/ModuleForm.tsx
New controlled forms for creating/editing programs and modules; module form includes debounced project autocomplete.
Frontend: GraphQL operations
frontend/src/server/queries/{programsQueries,moduleQueries,mentorshipQueries,projectQueries}.ts, .../mutations/{programsMutations,moduleMutations}.ts, .../server/apolloClient.ts, .../utils/helpers/apolloClient.ts
Adds queries/mutations for programs/modules/mentorship roles/projects; minor whitespace tweaks in Apollo client files.
Frontend: Shared UI components
frontend/src/components/{Card.tsx,CardDetailsPage.tsx,ModuleCard.tsx,SingleModuleCard.tsx,ProgramCard.tsx,ProgramActions.tsx,ModuleList.tsx,ToggleableList.tsx,Header.tsx,InfoItem.tsx,SearchPageLayout.tsx}
Enhances cards/details with timeline, modules, tags/domains, role-based actions; new Module/Program cards and actions; ModuleList with toggle; ToggleableList disable option; Header filters links by requiresGitHubAuth; adds TextInfoItem; makes empty prop optional.
Frontend: Types and utils
frontend/src/types/{mentorship,card,contributor,link}.ts, frontend/src/utils/{capitalize,dateFormatter,parser}.ts
Introduces mentorship domain types/enums; expands card/details types; adjusts Contributor optionals; link flag for GitHub auth; adds titleCaseWord, formatDateForInput, and parseCommaSeparated.
Frontend: Tests
frontend/__tests__/unit/**/*
Adds comprehensive unit tests for pages and components (programs/modules, actions, lists, cards, markdown), plus minor formatting/mocks updates.
Backend: Tests/config
backend/tests/apps/github/management/commands/github_match_users_test.py, backend/pyproject.toml
Test formatting cleanup; coverage config excludes mentorship path.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Possibly related PRs

Suggested reviewers

  • kasya
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Rajgupta36 Rajgupta36 self-assigned this Jul 6, 2025
@Rajgupta36 Rajgupta36 marked this pull request as draft July 6, 2025 21:37
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 both signIn and jwt callbacks, which is redundant and could cause performance issues.

Consider removing the GraphQL call from the signIn callback since the jwt 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()} &rarr;{' '}
-           {new Date(timeline.end).toLocaleDateString()}
+           {new Date(timeline.start).toLocaleDateString()} &rarr;{' '}
+           {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 since createApolloClient() returns an ApolloClient instance or null, not a Promise.

-const apolloClientPromise = createApolloClient()
+const apolloClient = createApolloClient()

-export default apolloClientPromise
+export default apolloClient

If 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 between all_programs and program 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 and started_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 = None
frontend/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 | null
frontend/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 in GET_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

📥 Commits

Reviewing files that changed from the base of the PR and between b8c0bed and 468f0b8.

⛔ 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 of capitalize—please verify behavior change

The 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.tsx

Next 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 and projectKey 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 Confirmed

The CI workflows specify python-version: '3.13' (both in .github/workflows/test-schema.yaml and run-ci-cd.yaml), which is ≥ 3.9. Since str.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
The CreateProgramInput requires started_at and ended_at, matching their definitions in StartEndRange (non-nullable DateTimeFields). Only if your intention is to allow draft programs without dates should you:

  • update backend/apps/mentorship/models/common/start_end_range.py to DateTimeField(null=True, blank=True)
  • make started_at and/or ended_at optional in CreateProgramInput

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 in CreateModuleInput (line 41) but optional in ModuleNode (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, and UPDATE_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 a project_id field, but it's not being set in the return value. This is inconsistent with the get_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 returned ModuleNode.

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 from next-auth/react is optimized for client-side usage and makes an extra API call when used server-side. For server-side Apollo client setup, use getServerSession from next-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 tsx
frontend/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

📥 Commits

Reviewing files that changed from the base of the PR and between 468f0b8 and a3c5d3b.

⛔ 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 & TRY003

Adding these three rules to the global ignore list disables Ruff checks that flag dubious exception-handling patterns (flake8-errmsg and tryceratops). That can silently permit:

• Poorly formatted or non-parameterised error messages (EM101)
• Broad/bare except blocks, pass in except, or forgotten raise … from exc chains (TRY002/TRY003)

Before merging, confirm that:

  1. The new mentorship code actually needs these rules suppressed across the entire codebase.
  2. 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 to any.


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

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 accesses self.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 err
frontend/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

📥 Commits

Reviewing files that changed from the base of the PR and between a3c5d3b and dbed9b9.

📒 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 and DetailsCardProps 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 the signIn and jwt callbacks.

backend/apps/mentorship/graphql/queries/module.py (1)

22-26: Excellent query optimization.

The use of select_related("project") and prefetch_related("mentors__github_user") demonstrates good understanding of Django ORM optimization to avoid N+1 query problems.

@Rajgupta36 Rajgupta36 marked this pull request as ready for review July 7, 2025 18:46
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between ad1d76f and 779df27.

📒 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)

Copy link
Collaborator

@arkid15r arkid15r left a 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?

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 pagination

limit 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 change

Program 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 2

Expected: 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 errors

Avoid 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 err

Also 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 requests

Confirm 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 405

For 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 search

Consider 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 empty

Either 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 iexact

iexact 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 races

Two 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

📥 Commits

Reviewing files that changed from the base of the PR and between df539bc and 9674fb0.

📒 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" in backend/apps/core/constants.py, matching the previous literal.
  • clear_index_cache in backend/apps/core/utils/index.py uses the same CACHE_PREFIX for its key pattern (f"{CACHE_PREFIX}:{index_name}*"), so cache-clear tooling remains aligned.
  • validate_search_params (in backend/apps/core/validators.py) calls validate_limit and validate_page, ensuring hitsPerPage and page are within allowed bounds and will raise ValidationError on invalid input. It also validates indexName is non-empty via validate_index_name.

Nit: For 405 responses, prefer returning HttpResponseNotAllowed([...]) instead of a generic JsonResponse.

No further action needed on imports or validation logic.

backend/apps/mentorship/index/registry/program.py (1)

23-44: No change needed: retain indexLanguages
Algolia’s API uses indexLanguages to configure language-specific processing at index time, while queryLanguages applies only at query time (stop words, plurals, etc.). All existing registries correctly use indexLanguages, so you can ignore the suggestion to switch to queryLanguages.

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.")

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 removed

Good move to source ExtendedSession from central types.


61-71: Resolved: no longer throwing inside effect catch

Thanks for switching to toasts instead of throwing; avoids crashes.

🧹 Nitpick comments (1)
frontend/src/hooks/useDjangoSession.ts (1)

81-81: Nit: use property shorthand

Minor cleanup.

-    status: status,
+    status,
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9674fb0 and c522b1c.

⛔ 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)

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between c522b1c and f66ddde.

📒 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. The IS_PROJECT_LEADER_QUERY and IS_MENTOR_QUERY are legitimate query operations that retrieve boolean values based on login parameters.

@Rajgupta36 Rajgupta36 requested review from arkid15r and kasya August 10, 2025 11:31
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 376ccdb and dc97d9a.

📒 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.

kasya
kasya previously approved these changes Aug 12, 2025
Copy link
Collaborator

@kasya kasya left a 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!

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
3.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@arkid15r arkid15r added this pull request to the merge queue Aug 13, 2025
Merged via the queue into OWASP:main with commit e2fc237 Aug 13, 2025
23 of 24 checks passed
ahmedxgouda pushed a commit to ahmedxgouda/Nest that referenced this pull request Aug 16, 2025
* 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]>
@arkid15r arkid15r mentioned this pull request Sep 9, 2025
2 tasks
@coderabbitai coderabbitai bot mentioned this pull request Sep 23, 2025
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants