Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intro Course Developer Profile Instructor View #256

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

niclasheun
Copy link
Contributor

@niclasheun niclasheun commented Mar 12, 2025

This PR introduces a Lecturer View for the Developer Profile.

This does not yet include the possibility to automatically create Gitlab Projects or add students to the apple developer account, but this will follow in the next PR.

image

Summary by CodeRabbit

  • New Features

    • Introduced a dedicated "Developer Profiles" page accessible via the sidebar.
    • Enabled lecturers to view, filter, and sort developer profiles by survey status and device ownership.
    • Added a user-friendly interface for editing and updating profile details.
    • Improved backend support for streamlined profile creation and updates.
    • Added a new route for accessing developer profiles with specific permissions.
    • Introduced new components for managing filters and profile details.
  • Bug Fixes

    • Corrected JSON tag casing for consistency in developer profile representations.

@Mtze Mtze temporarily deployed to prompt-dev-vm March 12, 2025 10:39 — with GitHub Actions Inactive
Copy link
Member

@Mtze Mtze left a comment

Choose a reason for hiding this comment

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

As an instructor i get an 400 if i place an invalid UUID - I Think we should add the same client side input validation to the instructor from
Screenshot 2025-03-12 at 13 16 41

Copy link

coderabbitai bot commented Mar 12, 2025

Walkthrough

The pull request introduces a new route and sidebar link for accessing developer profiles. It updates the developer profile types to include additional properties and introduces a new PostDeveloperProfile type. The changes propagate through API calls, mutation functions, and form components, while several new pages, components, and hooks are added for managing, filtering, and sorting developer profiles. Backend modifications include SQL upsert functionality, new endpoints, and service methods to support the updated data model.

Changes

File(s) Change Summary
clients/intro_course_developer_component/routes/index.tsx
clients/intro_course_developer_component/sidebar/index.tsx
Added a new /developer-profiles route and sidebar subitem linking to it.
clients/intro_course_developer_component/src/introCourse/interfaces/DeveloperProfile.tsx
clients/intro_course_developer_component/src/introCourse/interfaces/PostDeveloperProfile.tsx
Updated the DeveloperProfile type with properties coursePhaseID and courseParticipationID and added a new PostDeveloperProfile type.
clients/intro_course_developer_component/src/introCourse/network/mutations/postDeveloperProfile.ts
clients/intro_course_developer_component/src/introCourse/network/mutations/updateDeveloperProfile.ts
clients/intro_course_developer_component/src/introCourse/network/queries/getAllDeveloperProfiles.ts
Changed API functions to use PostDeveloperProfile, added an update function (PUT) and a function to fetch all developer profiles (GET).
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfile/DeveloperProfileForm.tsx
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfile/DeveloperProfilePage.tsx
Updated form and page types to use PostDeveloperProfile and streamlined the mutation process by removing an intermediary submission function.
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/DeveloperProfilesLecturerPage.tsx
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/components/FilterMenu.tsx
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/components/ProfileDetailsDialog.tsx
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/hooks/*
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/interfaces/devProfileFilter.ts
Introduced a new lecturer page with multiple components, hooks, and interfaces for filtering, sorting, and managing developer profiles.
clients/intro_course_developer_component/src/introCourse/validations/instructorDevProfile.ts Added a new validation schema with Zod for instructor developer profiles and its inferred type.
servers/intro_course/db/query/developerProfile.sql
servers/intro_course/db/sqlc/developerProfile.sql.go
Added a new SQL command with upsert functionality and a corresponding Go function with a new parameters struct for creating or updating developer profiles.
servers/intro_course/developerProfile/developerProfileDTO/* Corrected JSON serialization tags in DTOs and introduced a function to map create requests to database parameters.
servers/intro_course/developerProfile/router.go
servers/intro_course/developerProfile/service.go
Added a new PUT endpoint for updating developer profiles with updated routing and service functions for handling the operation.

Sequence Diagram(s)

sequenceDiagram
  participant UI as Frontend UI
  participant Client as API Function (updateDeveloperProfile)
  participant Server as Server Endpoint
  participant Service as DeveloperProfile Service
  participant DB as Database

  UI->>Client: Submit updated developer profile
  Client->>Server: PUT /developerProfile/:courseParticipationID
  Server->>Service: Call CreateOrUpdateDeveloperProfile(ctx, phaseID, participationID, profile)
  Service->>DB: Execute upsert SQL command
  DB-->>Service: Return confirmation
  Service-->>Server: Return response
  Server-->>Client: 200 OK / Error
  Client-->>UI: Update UI based on response
Loading

Poem

Hop, skip, and code away,
I'm a bunny with keys astray,
New routes and hooks in the digital fey,
Upsert and filter in a clever display,
Carrots and code, I celebrate the day!
🐰💻🌟

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
    • Generate unit testing code for this file.
    • 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. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

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

@niclasheun
Copy link
Contributor Author

Jetzt auch mit Validation für dich @Mtze

Copy link

@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: 4

🧹 Nitpick comments (14)
clients/intro_course_developer_component/src/introCourse/network/mutations/updateDeveloperProfile.ts (1)

19-22: Consider improving error handling.

The current error handling logs the error and then rethrows it, which might lead to duplicate error logging if the caller also logs the error.

Consider removing the console logging and let the caller handle errors:

  } catch (err) {
-   console.error(err)
    throw err
  }

Alternatively, add more context to the error for better debugging:

  } catch (err) {
-   console.error(err)
+   console.error(`Failed to update developer profile: ${err.message}`, err)
    throw err
  }
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/hooks/useGetSortedParticipations.tsx (1)

1-40: Well-structured custom hook for sorting

The implementation of useGetSortedParticipations is clean and follows React best practices with proper use of useMemo for optimization.

However, I'd recommend adding input validation to handle edge cases:

export const useGetSortedParticipations = (
  sortConfig:
    | {
        key: string
        direction: 'ascending' | 'descending'
      }
    | undefined,
  participantsWithProfiles: {
    participation: CoursePhaseParticipationWithStudent
    profile: DeveloperProfile | undefined
  }[],
) => {
  return useMemo(() => {
+   if (!participantsWithProfiles || participantsWithProfiles.length === 0) {
+     return []
+   }
    const sorted = [...participantsWithProfiles]
    if (!sortConfig) return sorted

    return sorted.sort((a, b) => {
      let aValue: string | number = ''
      let bValue: string | number = ''

      if (sortConfig.key === 'name') {
        aValue =
          `${a.participation.student.firstName} ${a.participation.student.lastName}`.toLowerCase()
        bValue =
          `${b.participation.student.firstName} ${b.participation.student.lastName}`.toLowerCase()
      } else if (sortConfig.key === 'profileStatus') {
        aValue = a.profile ? 1 : 0
        bValue = b.profile ? 1 : 0
      }

      if (aValue < bValue) return sortConfig.direction === 'ascending' ? -1 : 1
      if (aValue > bValue) return sortConfig.direction === 'ascending' ? 1 : -1
      return 0
    })
  }, [participantsWithProfiles, sortConfig])
}
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/hooks/useGetParticipationsWithProfiles.tsx (1)

5-22: Consider adding explicit return type and removing unnecessary optional chaining.

The hook correctly maps participants to their profiles, but could benefit from two improvements:

  1. Add an explicit return type for better type safety
  2. Remove unnecessary optional chaining on developerProfiles (line 13) as it's already a parameter
  3. Remove the unnecessary fallback empty array (line 19) as the mapping would already handle an empty array
export const useGetParticipationsWithProfiles = (
  participants: CoursePhaseParticipationWithStudent[],
  developerProfiles: DeveloperProfile[],
+) : { participation: CoursePhaseParticipationWithStudent, profile: DeveloperProfile | undefined }[] => {
  return useMemo(() => {
    return (
      participants.map((participation) => {
        const profile =
-         developerProfiles?.find(
+         developerProfiles.find(
            (devProfile) =>
              devProfile.courseParticipationID === participation.courseParticipationID,
          ) || undefined

        return { participation, profile }
-     }) || []
+     })
    )
  }, [participants, developerProfiles])
}
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/components/FilterMenu.tsx (3)

29-50: Implement consistent event handling throughout the component.

The survey status filter items use onCheckedChange while device filters use custom onClick handlers with e.preventDefault(). This inconsistency should be addressed.

Consider using the same approach for all filter items for consistency:

<DropdownMenuCheckboxItem
  checked={filters.devices.macBook}
- onClick={(e) => {
-   e.preventDefault()
+ onCheckedChange={(checked) => 
    setFilters((prevFilters) => ({
      ...filters,
      devices: {
        ...prevFilters.devices,
        noDevices: false,
-       macBook: !prevFilters.devices.macBook,
+       macBook: checked,
      },
    }))
- }}
+ }
>

57-67: Fix potential stale state reference in the state update.

When updating state with the previous state, consistently use the previous state reference in all spread operations.

setFilters((prevFilters) => ({
-  ...filters,
+  ...prevFilters,
  devices: {
    ...prevFilters.devices,
    noDevices: false,
    macBook: !prevFilters.devices.macBook,
  },
}))

124-140: Consider extracting "No Devices" logic to a separate function.

The reset logic for the "No Devices" option could be extracted to improve readability.

Consider extracting this logic:

const handleNoDevicesChange = (e: React.MouseEvent) => {
  e.preventDefault()
  setFilters((prevFilters) => ({
    ...prevFilters,
    devices: {
      noDevices: !prevFilters.devices.noDevices,
      macBook: false,
      iPhone: false,
      iPad: false,
      appleWatch: false,
    },
  }))
}
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/DeveloperProfilesLecturerPage.tsx (4)

41-47: Avoid using any type - replace with specific type for better type safety.

The any type for participation should be replaced with a more specific type like CoursePhaseParticipationWithStudent.

const [selectedParticipant, setSelectedParticipant] = useState<
  | {
-     participation: any
+     participation: CoursePhaseParticipationWithStudent
      profile: DeveloperProfile | undefined
    }
  | undefined
>(undefined)

150-166: Enhance accessibility for sortable table headers.

Add proper ARIA attributes to indicate that these table headers are sortable to improve accessibility.

<TableHead 
  className='cursor-pointer' 
  onClick={() => requestSort('name')}
+ aria-sort={sortConfig?.key === 'name' ? sortConfig.direction : 'none'}
+ role="columnheader"
+ aria-label="Sort by name"
>

Also applies to: 167-186


98-101: Consider enhancing the refresh function to handle errors.

The handleRefresh function refetches both data sources but doesn't handle potential errors during refetching.

const handleRefresh = () => {
-  refetchCoursePhaseParticipations()
-  refetchDeveloperProfiles()
+  Promise.all([
+    refetchCoursePhaseParticipations(),
+    refetchDeveloperProfiles()
+  ]).catch((error) => {
+    console.error('Error refreshing data:', error)
+    // Consider showing a toast or notification to the user
+  })
}

1-267: Consider breaking down this large component into smaller components.

This page component is quite large (267 lines) and handles multiple concerns including state management, API calls, filtering, sorting, and rendering. Consider breaking it down into smaller, more focused components or custom hooks for better maintainability.

You could extract:

  1. A ParticipantTable component for the table rendering
  2. A ParticipantRow component for each row
  3. A useDeveloperProfilesData hook for data fetching logic

This would make the main component more focused on composition and higher-level concerns.

clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/hooks/useGetFilteredParticipations.tsx (2)

13-29: Consider refactoring the survey status filter logic

The survey status filter logic works correctly but could be simplified for better readability.

- const surveyFilterActive = filters.surveyStatus.completed || filters.surveyStatus.notCompleted
- let passesSurvey = true
- if (surveyFilterActive) {
-   // Assume false initially then try matching any active filter.
-   passesSurvey = false
-   if (filters.surveyStatus.completed && profile) {
-     passesSurvey = true
-   }
-   if (filters.surveyStatus.notCompleted && !profile) {
-     passesSurvey = true
-   }
- }
+ const surveyFilterActive = filters.surveyStatus.completed || filters.surveyStatus.notCompleted
+ const passesSurvey = !surveyFilterActive || 
+   (filters.surveyStatus.completed && profile) || 
+   (filters.surveyStatus.notCompleted && !profile)

34-45: Use optional chaining for safer property access

The current implementation uses a verbose pattern to check if profile exists before accessing its properties. Using optional chaining would make the code more concise and readable.

The static analysis tool correctly identified this issue. Apply this change to all device checks:

- passesDevices = passesDevices && !!(profile && profile.hasMacBook)
+ passesDevices = passesDevices && !!profile?.hasMacBook

- passesDevices = passesDevices && !!(profile && profile.iPhoneUUID)
+ passesDevices = passesDevices && !!profile?.iPhoneUUID

- passesDevices = passesDevices && !!(profile && profile.iPadUUID)
+ passesDevices = passesDevices && !!profile?.iPadUUID

- passesDevices = passesDevices && !!(profile && profile.appleWatchUUID)
+ passesDevices = passesDevices && !!profile?.appleWatchUUID
🧰 Tools
🪛 Biome (1.9.4)

[error] 35-35: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 38-38: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 41-41: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 44-44: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

servers/intro_course/db/sqlc/developerProfile.sql.go (1)

67-76: Consider consolidating duplicate parameter structs

The CreateOrUpdateDeveloperProfileParams struct is identical to the existing CreateDeveloperProfileParams struct. While this is auto-generated code, it might be worth considering a refactoring of the SQL queries to use a common parameter struct if possible.

servers/intro_course/developerProfile/router.go (1)

31-32: Fix typo in comment

There's a small typo in the comment.

- // ger course participation id from context
+ // get course participation id from context
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c2e037 and 4ad8b2a.

📒 Files selected for processing (23)
  • clients/intro_course_developer_component/routes/index.tsx (2 hunks)
  • clients/intro_course_developer_component/sidebar/index.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/interfaces/DeveloperProfile.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/interfaces/PostDeveloperProfile.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/network/mutations/postDeveloperProfile.ts (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/network/mutations/updateDeveloperProfile.ts (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/network/queries/getAllDeveloperProfiles.ts (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfile/DeveloperProfileForm.tsx (3 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfile/DeveloperProfilePage.tsx (3 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/DeveloperProfilesLecturerPage.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/components/FilterMenu.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/components/ProfileDetailsDialog.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/hooks/useGetFilteredParticipations.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/hooks/useGetParticipationsWithProfiles.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/hooks/useGetSortedParticipations.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/interfaces/devProfileFilter.ts (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/validations/instructorDevProfile.ts (1 hunks)
  • servers/intro_course/db/query/developerProfile.sql (1 hunks)
  • servers/intro_course/db/sqlc/developerProfile.sql.go (1 hunks)
  • servers/intro_course/developerProfile/developerProfileDTO/get_developer_profile.go (2 hunks)
  • servers/intro_course/developerProfile/developerProfileDTO/post_developer_profile.go (2 hunks)
  • servers/intro_course/developerProfile/router.go (2 hunks)
  • servers/intro_course/developerProfile/service.go (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/hooks/useGetFilteredParticipations.tsx

[error] 35-35: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 38-38: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 41-41: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 44-44: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: build-servers / core / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-core
  • GitHub Check: build-servers / intro-course / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-intro-course
  • GitHub Check: build-clients / clients-base / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-clients-base
  • GitHub Check: build-servers / core / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-core
  • GitHub Check: build-clients / clients-base / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-clients-base
  • GitHub Check: build-servers / intro-course / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-intro-course
  • GitHub Check: Mend Security Check
🔇 Additional comments (34)
clients/intro_course_developer_component/src/introCourse/interfaces/DeveloperProfile.tsx (1)

1-10: New properties added align well with the PR objectives

The addition of coursePhaseID and courseParticipationID to the DeveloperProfile type is appropriate for the Lecturer View implementation. These fields will allow for proper association of developer profiles with specific course phases and student participations.

clients/intro_course_developer_component/src/introCourse/network/queries/getAllDeveloperProfiles.ts (1)

4-17: Query function implementation looks good

The implementation of getAllDeveloperProfiles follows a clean pattern with proper error handling. It correctly uses the introCourseAxiosInstance for consistency with other API calls in the project.

clients/intro_course_developer_component/src/introCourse/interfaces/PostDeveloperProfile.tsx (1)

1-8: Clean separation of concerns with PostDeveloperProfile type

Creating a separate PostDeveloperProfile type that excludes server-side identifiers like coursePhaseID and courseParticipationID is a good design decision. This maintains a clear distinction between the data model used for displaying profiles and the one used for submitting updates.

clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/interfaces/devProfileFilter.ts (1)

1-13: Well-designed filter structure.

This type definition provides a clear interface for filtering developer profiles by survey status and device ownership. The boolean flags make it easy to toggle filters in the UI.

servers/intro_course/db/query/developerProfile.sql (1)

17-36: Good implementation of SQL upsert operation.

The SQL command is well-structured with proper conflict handling on the composite key (course_phase_id, course_participation_id). This approach efficiently handles both creation and updates in a single database operation.

clients/intro_course_developer_component/src/introCourse/network/mutations/updateDeveloperProfile.ts (1)

4-23: Consider return type discrepancy and content type.

Two issues to consider:

  1. The function has a return type of Promise<void> but returns the axios response object, which would be Promise<AxiosResponse<any>>.
  2. The Content-Type header uses application/json-path+json which is uncommon - standard REST APIs typically use application/json.

Does the backend specifically require application/json-path+json content type, or would application/json work? Also, confirm whether you need to return data from this API call or if void is the correct return type.

export const updateDeveloperProfile = async (
  coursePhaseID: string,
  courseParticipationID: string,
  developerProfile: PostDeveloperProfile,
-): Promise<void> => {
+): Promise<any> => {
  try {
    return await introCourseAxiosInstance.put(
      `intro-course/api/course_phase/${coursePhaseID}/developer_profile/${courseParticipationID}`,
      developerProfile,
      {
        headers: {
-         'Content-Type': 'application/json-path+json',
+         'Content-Type': 'application/json',
        },
      },
    )
  } catch (err) {
    console.error(err)
    throw err
  }
}
servers/intro_course/developerProfile/service.go (1)

81-96: Implementation follows established pattern for data operations

The new CreateOrUpdateDeveloperProfile function follows the same pattern as other functions in this service, properly handling context, error logging, and return values. It correctly implements an upsert operation that can both create new developer profiles and update existing ones.

One observation: this new function accepts a DeveloperProfile type while the existing CreateDeveloperProfile function uses PostDeveloperProfile. This distinction suggests they serve different purposes in the API design.

clients/intro_course_developer_component/src/introCourse/validations/instructorDevProfile.ts (1)

1-22: Good validation implementation with proper type handling

The validation schema correctly defines required fields and appropriate validations:

  • Email validation for appleID
  • Required fields for core properties
  • Smart handling of optional UUID fields with preprocessing of empty strings

This approach will provide clear error messages during form validation while maintaining type safety throughout the application.

clients/intro_course_developer_component/src/introCourse/network/mutations/postDeveloperProfile.ts (1)

1-1: Type update aligns with data model changes

Updating the import and parameter type from DeveloperProfile to PostDeveloperProfile ensures consistency with the updated data model. This change supports the new instructor view functionality described in the PR objectives.

Also applies to: 6-6

servers/intro_course/developerProfile/developerProfileDTO/post_developer_profile.go (2)

12-12: Fixed JSON field casing consistency

Good correction to the JSON tag to ensure consistent casing between the backend and frontend (hasMacBook instead of hasMacbook). This avoids potential data mapping issues.


31-42: Well-structured DTO mapping for upsert operation

The new GetDeveloperProfileDTOFromCreateRequest function properly maps the DeveloperProfile fields to the database parameters required for the upsert operation. The implementation correctly handles all the necessary fields and maintains consistency with the existing pattern.

clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfile/DeveloperProfilePage.tsx (3)

9-9: Import updated to use PostDeveloperProfile type

This change correctly updates the import to use the new PostDeveloperProfile type instead of the previous DeveloperProfile type, aligning with the revised data model for submitting developer profiles.


24-25: Method signature updated for mutation function

The mutation function now correctly expects a PostDeveloperProfile type rather than DeveloperProfile, ensuring type consistency throughout the submission flow.


42-42: Simplified submission flow

The code now directly calls mutation.mutate(profile) instead of using an intermediary function, reducing unnecessary abstraction and making the code more maintainable.

clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfile/DeveloperProfileForm.tsx (4)

3-3: Import updated to use PostDeveloperProfile type

This change correctly updates the import statement to use the new PostDeveloperProfile type.


21-21: Appropriate import of DeveloperProfile type

Adding the import for DeveloperProfile is necessary since it's still used in the component props for displaying existing profiles.


26-26: Updated prop signature to use PostDeveloperProfile

The onSubmit prop type is correctly updated to use PostDeveloperProfile instead of DeveloperProfile, maintaining type consistency with the parent component.


59-59: Updated variable type to PostDeveloperProfile

The submittedProfile variable is now correctly typed as PostDeveloperProfile, ensuring type consistency throughout the submission flow.

servers/intro_course/developerProfile/developerProfileDTO/get_developer_profile.go (1)

14-14: Fixed JSON tag casing consistency

The JSON tag has been corrected from hasMacbook to hasMacBook to ensure consistent casing in the JSON representation.

clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/components/ProfileDetailsDialog.tsx (4)

43-81: Good implementation of the dialog form with error handling.

The React Hook Form setup with Zod validation is well-implemented, and the error handling in the mutation is thorough with proper error message extraction and display.


83-85: Form submission handler looks good.

The submission handler correctly passes the form data to the mutation function.


100-104: Well-implemented error display.

The error message display is properly implemented with appropriate styling.


106-225: Form structure is well-organized with appropriate UI components.

The form layout is clean with good organization of fields into logical sections. The device section with icons enhances usability.

clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/DeveloperProfilesLecturerPage.tsx (2)

128-136: Good implementation of the sorting function.

The sorting logic is well-implemented with appropriate toggling of the sorting direction.


139-264: Well-structured table with clear display of profile information.

The table layout is clean with good organization of data. The filtered count information and the device icons enhance usability. The dialog integration is also well implemented.

clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/hooks/useGetFilteredParticipations.tsx (4)

1-5: Clean import structure

The imports are well-organized and appropriately typed, making dependencies clear.


6-12: Well-typed hook parameters

The hook is properly typed with explicit interface definitions, which improves type safety and code readability.


46-55: Clear implementation of "No Devices" filter logic

The implementation correctly handles the "No Devices" filter by checking that the profile either doesn't exist or doesn't have any of the devices. The comment clearly explains the logic.


57-60: Efficient memoization with appropriate dependencies

The hook correctly uses useMemo with the proper dependencies array, ensuring the filtered results are only recalculated when necessary.

servers/intro_course/db/sqlc/developerProfile.sql.go (2)

45-65: Well-structured SQL upsert query

The createOrUpdateDeveloperProfile SQL query is well-formatted and properly implements the upsert pattern using the ON CONFLICT clause. The consistent indentation and column alignment make it easy to read.


78-90: Implementation follows established patterns consistently

The CreateOrUpdateDeveloperProfile function follows the same implementation pattern as other functions in the file, maintaining consistency in the codebase.

servers/intro_course/developerProfile/router.go (3)

19-20: New route follows authentication and authorization patterns

The implementation of the PUT endpoint correctly follows the pattern established by other routes, requiring appropriate authentication through the middleware.


92-119: Comprehensive error handling in update endpoint

The updateDeveloperProfile function follows good practices by:

  1. Properly parsing and validating UUID parameters
  2. Providing descriptive error logs
  3. Using appropriate HTTP status codes for different error conditions
  4. Following the same error handling pattern as other endpoints

113-113:

✅ Verification successful

Verify CreateOrUpdateDeveloperProfile implementation

Make sure that the CreateOrUpdateDeveloperProfile function is properly implemented in a separate file, as it's being called here but not defined within this file.


🏁 Script executed:

#!/bin/bash
# Check for implementation of CreateOrUpdateDeveloperProfile function
grep -r "func CreateOrUpdateDeveloperProfile" --include="*.go" servers/intro_course/

Length of output: 304


Implementation Verified
The CreateOrUpdateDeveloperProfile function has been confirmed to be implemented in the separate file servers/intro_course/developerProfile/service.go. No further changes are required based on this review.

Comment on lines 11 to 14
{
title: 'Developer Profiles',
goToPath: '/developer-profiles',
},
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing required permissions for Developer Profiles sidebar item

Unlike other sidebar items like "Tutor Import" and "Settings" which have requiredPermissions specified, the new "Developer Profiles" item lacks permission restrictions. Consider whether this is intentional, as the PR description mentions this is a "Lecturer View" which suggests it should have similar permission restrictions to the other lecturer-specific items.

    {
      title: 'Developer Profiles',
      goToPath: '/developer-profiles',
+     requiredPermissions: [Role.PROMPT_ADMIN, Role.COURSE_LECTURER],
    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
title: 'Developer Profiles',
goToPath: '/developer-profiles',
},
{
title: 'Developer Profiles',
goToPath: '/developer-profiles',
requiredPermissions: [Role.PROMPT_ADMIN, Role.COURSE_LECTURER],
},

Comment on lines 19 to 22
{
path: '/developer-profiles',
element: <DeveloperProfilesLecturerPage />,
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security concern: Missing required permissions for developer profiles route.

Unlike other routes in this file, the new developer profiles route doesn't specify requiredPermissions. Since this is labeled as a "Lecturer View" in the PR description, it should likely have the same permission requirements as other lecturer-only routes.

  {
    path: '/developer-profiles',
    element: <DeveloperProfilesLecturerPage />,
+   requiredPermissions: [Role.PROMPT_ADMIN, Role.COURSE_LECTURER],
  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
path: '/developer-profiles',
element: <DeveloperProfilesLecturerPage />,
},
{
path: '/developer-profiles',
element: <DeveloperProfilesLecturerPage />,
requiredPermissions: [Role.PROMPT_ADMIN, Role.COURSE_LECTURER],
},

Copy link

@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

🧹 Nitpick comments (6)
servers/intro_course/developerProfile/router.go (3)

92-119: Implementation follows established patterns but lacks documentation

The updateDeveloperProfile function correctly handles parameter parsing, error checking, and delegates to the service layer, following the same pattern as other handlers in this file.

Consider adding documentation comments for this function to maintain consistency with the codebase style.

+// updateDeveloperProfile handles PUT requests to update an existing developer profile
+// This endpoint is restricted to users with PromptAdmin or CourseLecturer roles
 func updateDeveloperProfile(c *gin.Context) {

107-111: Consider adding validation for the developer profile data

While the function correctly binds JSON to the devProfile struct, there's no validation of the data beyond ensuring it can be parsed from JSON. Consider adding validation to ensure the data meets your business requirements before updating the database.

 var devProfile developerProfileDTO.DeveloperProfile
 if err := c.BindJSON(&devProfile); err != nil {
 	handleError(c, http.StatusBadRequest, err)
 	return
 }
+
+// Validate required fields
+if err := validateDeveloperProfile(devProfile); err != nil {
+	handleError(c, http.StatusBadRequest, err)
+	return
+}

113-118: Consider returning the updated profile in the response

Other endpoints like getOwnDeveloperProfile return the profile data in the response, but this endpoint only returns a status code. For consistency and to help clients avoid an additional API call, consider returning the updated profile in the response.

 err = CreateOrUpdateDeveloperProfile(c, coursePhaseID, courseParticipationID, devProfile)
 if err != nil {
 	handleError(c, http.StatusInternalServerError, err)
 	return
 }
-c.Status(http.StatusOK)
+
+// Return the updated profile
+updatedProfile, err := GetDeveloperProfile(c, coursePhaseID, courseParticipationID)
+if err != nil {
+	handleError(c, http.StatusInternalServerError, err)
+	return
+}
+c.JSON(http.StatusOK, updatedProfile)
clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/DeveloperProfilesLecturerPage.tsx (3)

30-41: Keep naming consistent in custom hooks.
Your naming structure (useGetParticipationsWithProfiles, useGetSortedParticipations, useGetFilteredParticipations) is clear. Ensure that you maintain consistency and a predictable naming scheme across your codebase so that future collaborators can easily locate related logic.


42-75: Consider merging sort and filter states if needed.
You currently maintain separate states for sorting (sortConfig) and filtering (filters). If these configurations become more complex over time, consider bundling them in a single reducer or context for clarity.


130-139: Improve user feedback for sorting indication.
Your arrow icons clearly indicate sorting direction. For better accessibility, consider adding an aria-sort attribute on the clickable headers to help screen readers identify the current sort state.

<TableHead
  className='cursor-pointer'
  onClick={() => requestSort('name')}
+ aria-sort={sortConfig?.key === 'name' ? sortConfig.direction : 'none'}>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ad8b2a and bf1b70e.

📒 Files selected for processing (5)
  • clients/intro_course_developer_component/routes/index.tsx (2 hunks)
  • clients/intro_course_developer_component/sidebar/index.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/DeveloperProfilesLecturerPage.tsx (1 hunks)
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/components/FilterMenu.tsx (1 hunks)
  • servers/intro_course/developerProfile/router.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • clients/intro_course_developer_component/routes/index.tsx
  • clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/components/FilterMenu.tsx
  • clients/intro_course_developer_component/sidebar/index.tsx
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: build-servers / intro-course / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-intro-course
  • GitHub Check: build-clients / clients-base / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-clients-base
  • GitHub Check: build-clients / clients-base / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-clients-base
  • GitHub Check: build-servers / core / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-core
  • GitHub Check: build-servers / intro-course / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-intro-course
  • GitHub Check: build-servers / core / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-core
🔇 Additional comments (6)
servers/intro_course/developerProfile/router.go (2)

19-19: LGTM: New route added for developer profile updates

The new PUT route properly leverages the authentication middleware to restrict access to admin and lecturer roles, which aligns with the architectural pattern used throughout the router.


31-31: Comment wording corrected

Minor fix to comment wording from "ger" to "get" improves readability.

clients/intro_course_developer_component/src/introCourse/pages/DeveloperProfilesLecturer/DeveloperProfilesLecturerPage.tsx (4)

1-11: Use consistent import style and maintain logical grouping.
All imports look good and relevant, but consider grouping common dependencies together for consistency and readability (e.g. external libraries first, then local components/utilities).


12-26: Validate icon usage and handle missing icons gracefully if needed.
The icons from lucide-react are used properly for better visual feedback. Make sure that the icons are always rendered in consistent sizes and handle any potential scenario where the icon might fail to load or become unavailable in the future (unlikely, but worth a note if icons are externally sourced).


99-120: Error boundary usage is appropriate.
Returning an ErrorPage component upon failure with a refresh action is good. Ensure that the ErrorPage itself gracefully handles network or server failures and that any advanced error reporting is integrated if your app requires logs or telemetry.


193-267: Ensure that row clicks and “Edit” clicks are handled distinctly.
You call e.stopPropagation() in the button’s click handler—this is a standard approach to separate row-level and button-level actions. Just confirm that your QA team is aware of this subtlety to ensure consistent UX tests.

Comment on lines +76 to +98
// Get the developer profile and course phase participations
const { phaseId } = useParams<{ phaseId: string }>()
const {
data: coursePhaseParticipations,
isPending: isCoursePhaseParticipationsPending,
isError: isParticipationsError,
refetch: refetchCoursePhaseParticipations,
} = useQuery<CoursePhaseParticipationsWithResolution>({
queryKey: ['participants', phaseId],
queryFn: () => getCoursePhaseParticipations(phaseId ?? ''),
})

const {
data: developerProfiles,
isPending: isDeveloperProfilesPending,
isError: isDeveloperProfileError,
refetch: refetchDeveloperProfiles,
} = useQuery<DeveloperProfile[]>({
queryKey: ['developerProfiles', phaseId],
queryFn: () => getAllDeveloperProfiles(phaseId ?? ''),
})

const isError = isParticipationsError || isDeveloperProfileError
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Check for invalid phaseId usage.
You are defaulting phaseId to an empty string if it’s undefined. Double-check that downstream functions (getCoursePhaseParticipations, getAllDeveloperProfiles) handle an empty string gracefully. Eliminate potential edge cases if phaseId is required.

Use this script to search callers for possible usage issues:


🏁 Script executed:

#!/bin/bash
# Searching for all calls to getCoursePhaseParticipations and getAllDeveloperProfiles
rg -A 5 "getCoursePhaseParticipations|getAllDeveloperProfiles"

Length of output: 10984


PhaseId Validation Issue in API Queries

It appears that using phaseId ?? '' may inadvertently trigger API calls with an empty string when phaseId is missing. In the backend (e.g., in the Go router for getAllDeveloperProfiles), an empty string will cause uuid.Parse to fail, returning a 400 error. This pattern is found in multiple components. To address this issue, please ensure that:

  • The queries are not initiated when phaseId is falsy. For example, update your React Query configurations using the enabled option:

    useQuery({
      queryKey: ['participants', phaseId],
      queryFn: () => getCoursePhaseParticipations(phaseId!),
      enabled: !!phaseId,
    })
  • Downstream functions such as getCoursePhaseParticipations and getAllDeveloperProfiles are either updated to handle an empty string gracefully or are protected by ensuring that they are called only when phaseId is valid.

Review these changes thoroughly to eliminate potential edge cases when phaseId is required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

2 participants