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

feat: add possibility to delete courses #4282

Merged
merged 12 commits into from
Oct 3, 2024
Merged

feat: add possibility to delete courses #4282

merged 12 commits into from
Oct 3, 2024

Conversation

sjschlapbach
Copy link
Member

@sjschlapbach sjschlapbach commented Oct 1, 2024

Through the same interface as the archiving of courses (PR #4281 ), they can also be deleted. On deletion request, the user needs to separately confirm each of the applying deletion steps, before being able to actually trigger the deletion.

Screenshot 2024-10-02 at 00 10 31 Screenshot 2024-10-03 at 12 37 55

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a modal for course deletion, enhancing course management capabilities.
    • Added a confirmation component for deleting various course elements.
    • Implemented a summary view for courses, displaying key metrics.
  • Enhancements

    • Improved state management for selected course IDs when modals are closed.
    • Enhanced user notifications and localization for course deletion actions.
  • Bug Fixes

    • Resolved issues related to course deletion confirmation and state updates in the UI.

Copy link

aviator-app bot commented Oct 1, 2024

Current Aviator status

Aviator will automatically update this comment as the status of the PR changes.
Comment /aviator refresh to force Aviator to re-examine your PR (or learn about other /aviator commands).

This PR was merged manually (without Aviator). Merging manually can negatively impact the performance of the queue. Consider using Aviator next time.


See the real-time status of this PR on the Aviator webapp.
Use the Aviator Chrome Extension to see the status of your PR within GitHub.

Copy link

coderabbitai bot commented Oct 1, 2024

Warning

Rate limit exceeded

@sjschlapbach has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 8 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Files that changed from the base of the PR and between 5a3d53e and 74d9400.

📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

This pull request introduces several components related to course deletion management within the frontend application. A new CourseDeletionModal component is added to facilitate course deletions, integrating GraphQL for fetching course details and managing deletion confirmations. Additionally, the CourseArchiveModal component is updated to include a new prop for managing the selected course ID. The CourseDeletionConfirmations and CourseDeletionItem components are also introduced to handle user confirmations for deleting various course-related items. The CourseSelectionPage is modified to incorporate these new functionalities.

Changes

File Path Change Summary
apps/frontend-manage/src/components/courses/modals/CourseArchiveModal.tsx Added new prop setSelectedCourseId to CourseArchiveModalProps interface, updated the onClose handler to reset selectedCourseId to null.
apps/frontend-manage/src/components/courses/modals/CourseDeletionConfirmations.tsx Introduced a new component for handling course deletion confirmations, accepting props for summary, confirmations, and a state setter.
apps/frontend-manage/src/components/courses/modals/CourseDeletionItem.tsx Added a new component to facilitate individual course deletion, accepting props for label, confirmation state, and click handler.
apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx Introduced a modal component for course deletion, managing visibility and confirmations, and utilizing GraphQL for operations. Added interfaces for deletion confirmation types and modal props.
apps/frontend-manage/src/pages/courses/index.tsx Updated CourseSelectionPage to include a state variable and setter for the deletion modal, simplified course fetching logic, and adjusted layout to include deletion buttons for each course. Enhanced notification styling for no courses found.
cypress/cypress/e2e/E-course-workflow.cy.ts Removed test case for assigning random groups and expanded the test for archiving and deleting courses to cover more scenarios.
packages/graphql/src/graphql/ops/QGetCourseSummary.graphql Added new GraphQL query GetCourseSummary to retrieve a summary of a course by its ID, including various metrics.
packages/graphql/src/ops.schema.json Introduced a new object type CourseSummary to the GraphQL schema for summarizing course metrics.
packages/graphql/src/ops.ts Added CourseSummary type and getCourseSummary query to the GraphQL operations.
packages/graphql/src/public/client.json Added methods DeleteCourse and GetCourseSummary to the GraphQL client for course management.
packages/graphql/src/public/schema.graphql Introduced CourseSummary type and added deleteCourse mutation and getCourseSummary query to the GraphQL schema.
packages/graphql/src/public/server.json Added mutations and queries for DeleteCourse, GetCourseSummary, and GetFeedbacks, enhancing the GraphQL API capabilities.
packages/graphql/src/schema/course.ts Introduced ICourseSummary interface and CourseSummary object type for course metrics.
packages/graphql/src/services/courses.ts Added functions getCourseSummary and deleteCourse for retrieving course summaries and deleting courses, respectively.
packages/i18n/messages/de.ts Enhanced German localization for course management with new keys for deletion confirmations and notifications.
packages/i18n/messages/en.ts Enhanced English localization for course management with new keys for deletion confirmations and notifications.

Suggested reviewers

  • rschlaefli

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

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

Copy link

cypress bot commented Oct 1, 2024

klicker-uzh    Run #3097

Run Properties:  status check passed Passed #3097  •  git commit 456ab90021 ℹ️: Merge 74d9400c8feb916fda0cf795d87bd5a9ebec1b0f into 7c84a597a3258e0bfdf9dcda6bae...
Project klicker-uzh
Branch Review course-deletion
Run status status check passed Passed #3097
Run duration 09m 40s
Commit git commit 456ab90021 ℹ️: Merge 74d9400c8feb916fda0cf795d87bd5a9ebec1b0f into 7c84a597a3258e0bfdf9dcda6bae...
Committer Julius Schlapbach
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 2
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 47
View all changes introduced in this branch ↗︎

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: 8

🧹 Outside diff range and nitpick comments (19)
packages/graphql/src/graphql/ops/MDeleteCourse.graphql (1)

1-5: Consider enhancing the mutation's return value and adding a comment.

While the current implementation is functional, consider the following improvements:

  1. Return additional information about the deleted course. This could be useful for logging purposes or implementing an undo feature. For example:
mutation DeleteCourse($id: String!) {
  deleteCourse(id: $id) {
    id
    name
    deletedAt
  }
}
  1. Add a comment to explain the purpose of this mutation and any potential side effects. For example:
# Deletes a course with the given ID.
# This operation is irreversible and will also delete all associated data.
# Ensure proper authorization checks are implemented on the server side.
mutation DeleteCourse($id: String!) {
  deleteCourse(id: $id) {
    id
  }
}

These enhancements would improve the mutation's usability and maintainability.

packages/graphql/src/graphql/ops/QGetCourseSummary.graphql (1)

1-10: LGTM! Consider adding course identification to the response.

The GetCourseSummary query is well-structured and provides a comprehensive overview of various course components. It aligns well with the PR objectives for enhancing course management capabilities.

As a potential enhancement, consider including basic course identification (e.g., course name or ID) in the response. This could be helpful for clients to verify they're receiving data for the correct course, especially in scenarios where multiple course summaries might be fetched.

If you decide to include course identification, you could modify the query like this:

 query GetCourseSummary($courseId: String!) {
   getCourseSummary(courseId: $courseId) {
+    id
+    name
     numOfLiveQuizzes
     numOfPracticeQuizzes
     numOfMicroLearnings
     numOfGroupActivities
     numOfLeaderboardEntries
     numOfParticipantGroups
   }
 }
packages/graphql/src/schema/course.ts (1)

134-153: LGTM! Consider adding a total count field.

The new ICourseSummary interface and CourseSummary object type are well-implemented and align with the PR objectives. They provide a concise summary of course-related metrics without altering existing functionality.

Consider adding a totalComponents field that sums up all the individual counts. This could provide a quick overview of the course's overall content volume. Here's a suggested addition:

export interface ICourseSummary {
  numOfLiveQuizzes: number
  numOfPracticeQuizzes: number
  numOfMicroLearnings: number
  numOfGroupActivities: number
  numOfLeaderboardEntries: number
  numOfParticipantGroups: number
+ totalComponents: number
}

export const CourseSummary = CourseSummaryRef.implement({
  fields: (t) => ({
    numOfLiveQuizzes: t.exposeInt('numOfLiveQuizzes'),
    numOfPracticeQuizzes: t.exposeInt('numOfPracticeQuizzes'),
    numOfMicroLearnings: t.exposeInt('numOfMicroLearnings'),
    numOfGroupActivities: t.exposeInt('numOfGroupActivities'),
    numOfLeaderboardEntries: t.exposeInt('numOfLeaderboardEntries'),
    numOfParticipantGroups: t.exposeInt('numOfParticipantGroups'),
+   totalComponents: t.int({
+     resolve: (parent) =>
+       parent.numOfLiveQuizzes +
+       parent.numOfPracticeQuizzes +
+       parent.numOfMicroLearnings +
+       parent.numOfGroupActivities
+   }),
  }),
})

This addition would provide a convenient way to get the total number of learning components in a course.

packages/graphql/src/schema/query.ts (1)

199-208: LGTM: New getCourseSummary query field added correctly

The new query field getCourseSummary has been implemented correctly:

  • It's properly authenticated using asUser.field().
  • It returns a nullable CourseSummary type, which is appropriate.
  • It takes a required courseId argument.
  • The resolver function correctly calls the corresponding service method.

Consider adding error handling in the resolver function to catch and handle potential errors from CourseService.getCourseSummary(). This would improve the robustness of the API. For example:

resolve(_, args, ctx) {
  try {
    return CourseService.getCourseSummary(args, ctx)
  } catch (error) {
    console.error('Error fetching course summary:', error)
    throw new Error('Failed to retrieve course summary')
  }
}
packages/graphql/src/services/courses.ts (3)

620-653: LGTM! Consider adding error handling.

The getCourseSummary function is well-implemented. It efficiently retrieves course statistics using Prisma's _count feature and properly checks for course existence and ownership.

Consider adding error handling to catch and log any unexpected errors during the database query. This could help with debugging in case of database connection issues or other unforeseen problems.

export async function getCourseSummary(
  { courseId }: { courseId: string },
  ctx: ContextWithUser
) {
+ try {
    const course = await ctx.prisma.course.findUnique({
      // ... (existing query)
    })

    if (!course) return null

    return {
      // ... (existing return object)
    }
+ } catch (error) {
+   console.error('Error in getCourseSummary:', error)
+   throw new Error('Failed to retrieve course summary')
+ }
}

655-672: LGTM! Consider adding error handling and using a transaction.

The deleteCourse function is well-implemented, properly checking for course ownership before deletion and emitting an 'invalidate' event for cache consistency.

Consider the following improvements:

  1. Add error handling to catch and log any unexpected errors during the deletion process.
  2. Use a transaction to ensure atomicity of the delete operation and the event emission.

Here's a suggested implementation:

export async function deleteCourse(
  { id }: { id: string },
  ctx: ContextWithUser
) {
+ return ctx.prisma.$transaction(async (prisma) => {
+   try {
-     const deletedCourse = await ctx.prisma.course.delete({
+     const deletedCourse = await prisma.course.delete({
        where: {
          id,
          ownerId: ctx.user.sub,
        },
      })

      ctx.emitter.emit('invalidate', {
        typename: 'Course',
        id,
      })

      return deletedCourse
+   } catch (error) {
+     console.error('Error in deleteCourse:', error)
+     throw new Error('Failed to delete course')
+   }
+ })
}

This implementation ensures that if the deletion fails, the 'invalidate' event won't be emitted, maintaining consistency between the database and the cache.


620-672: Overall, the changes look good and align with the PR objectives.

The additions of getCourseSummary and deleteCourse functions enhance the course management capabilities as intended. They are well-implemented and follow good practices such as checking for course ownership and emitting events for cache invalidation.

To further improve the robustness of these functions, consider implementing a centralized error handling mechanism. This could involve creating a custom error class for course-related errors and using it consistently across all course management functions. For example:

class CourseError extends Error {
  constructor(message: string, public code: string) {
    super(message);
    this.name = 'CourseError';
  }
}

// Usage in functions:
throw new CourseError('Course not found', 'COURSE_NOT_FOUND');

This approach would make error handling more consistent and provide more detailed error information to the calling code.

packages/graphql/src/public/schema.graphql (3)

212-219: LGTM! Consider expanding the CourseSummary type.

The new CourseSummary type is well-structured and provides a good overview of the course's components. The use of non-nullable integers is appropriate for count fields.

Consider adding more fields to provide a more comprehensive summary, such as:

  • numOfParticipants: Int!
  • averageParticipantScore: Float
  • createdAt: Date!
  • updatedAt: Date

These additional fields could provide valuable insights into the course's overall status and performance.


768-768: LGTM! Consider enhancing error handling for deleteCourse mutation.

The deleteCourse mutation is well-defined and consistent with other delete operations in the schema.

Consider enhancing the mutation to handle potential error cases:

  1. Create a new union type for the return value:
union DeleteCourseResult = Course | DeleteCourseError

type DeleteCourseError {
  message: String!
  code: DeleteCourseErrorCode!
}

enum DeleteCourseErrorCode {
  COURSE_NOT_FOUND
  UNAUTHORIZED
  DELETION_FAILED
}
  1. Update the mutation to use the new return type:
deleteCourse(id: String!): DeleteCourseResult!

This approach would provide more detailed error information to the client, improving error handling and user experience.


1077-1077: LGTM! Consider enhancing error handling for getCourseSummary query.

The getCourseSummary query is well-defined and consistent with other query operations in the schema.

Consider enhancing the query to handle potential error cases:

  1. Create a new union type for the return value:
union CourseSummaryResult = CourseSummary | CourseSummaryError

type CourseSummaryError {
  message: String!
  code: CourseSummaryErrorCode!
}

enum CourseSummaryErrorCode {
  COURSE_NOT_FOUND
  UNAUTHORIZED
}
  1. Update the query to use the new return type:
getCourseSummary(courseId: String!): CourseSummaryResult!

This approach would provide more detailed error information to the client, improving error handling and user experience.

packages/graphql/src/schema/mutation.ts (1)

621-631: Consider additional safeguards for course deletion

While the deleteCourse mutation is correctly implemented, deleting a course is a significant operation that might have far-reaching consequences. Consider implementing the following safeguards:

  1. Add a confirmation mechanism, similar to the one mentioned in the PR objectives for archiving courses.
  2. Implement a check to ensure that the course is empty (no active students, sessions, or related data) before allowing deletion.
  3. Consider a soft delete approach instead of a hard delete, which would allow for potential recovery of accidentally deleted courses.

Would you like assistance in implementing these additional safeguards?

packages/graphql/src/public/server.json (1)

Line range hint 117-126: LGTM: New GetCourseSummary query added with a suggestion

The new GetCourseSummary query has been successfully added. It provides a comprehensive summary of course-related data, which is likely useful for displaying course information, possibly before deletion.

Consider adding a totalParticipants field to the summary, which would be the sum of numOfLeaderboardEntries and participants not on the leaderboard. This could provide a more complete picture of course engagement.

query GetCourseSummary($courseId: String!) {
  getCourseSummary(courseId: $courseId) {
    numOfLiveQuizzes
    numOfPracticeQuizzes
    numOfMicroLearnings
    numOfGroupActivities
    numOfLeaderboardEntries
    numOfParticipantGroups
+   totalParticipants
    __typename
  }
}
packages/graphql/src/ops.ts (2)

250-258: LGTM! Consider adding a total participants field.

The CourseSummary type is a great addition, providing useful metrics for course management. It aligns well with the PR objective of adding course deletion functionality.

Consider adding a totalParticipants field to provide a quick overview of the course size, which could be useful in deletion decisions.


2627-2632: LGTM! Consider expanding the DeleteCourseMutation response.

The DeleteCourseMutationVariables and DeleteCourseMutation types are correctly defined, maintaining consistency with the earlier definitions.

Consider expanding the DeleteCourseMutation response to include more information about the deleted course (e.g., name, deletion timestamp). This could provide more context in client applications using this mutation.

packages/graphql/src/ops.schema.json (3)

2442-2548: LGTM! Consider adding a total count field.

The new CourseSummary type is well-structured and provides a comprehensive overview of course components. This aligns well with the PR objective of enhancing course management capabilities, particularly for the course deletion feature.

Consider adding a totalComponents field that sums up all the individual counts. This could provide a quick overview of the course's overall complexity:

+          {
+            "name": "totalComponents",
+            "description": "Total number of components in the course",
+            "args": [],
+            "type": {
+              "kind": "NON_NULL",
+              "name": null,
+              "ofType": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              }
+            },
+            "isDeprecated": false,
+            "deprecationReason": null
+          }

9825-9853: LGTM! Consider adding error handling.

The deleteCourse mutation is well-defined and aligns perfectly with the PR objective of adding course deletion functionality. The input and output types are appropriate for the operation.

Consider adding error handling to provide more informative feedback. You could create a custom result type that includes both the deleted course and any potential error messages:

type DeleteCourseResult {
  course: Course
  success: Boolean!
  error: String
}

type Mutation {
  deleteCourse(id: String!): DeleteCourseResult!
}

This structure allows for more detailed error reporting while still providing the deleted course data on success.


16516-16544: LGTM! Consider renaming for consistency.

The getCourseSummary query is well-defined and provides a clear way to fetch summary data for a specific course. This complements the CourseSummary type introduced earlier and supports the enhanced course management capabilities.

For consistency with other query names in the schema, consider renaming the query to courseSummary. This follows the convention of using noun phrases for queries rather than verb phrases:

-            "name": "getCourseSummary",
+            "name": "courseSummary",

This change would align the query name with GraphQL naming conventions and maintain consistency with other queries in your schema.

apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (2)

123-126: Simplify the Disabled Condition for the Confirm Button

The condition for disabling the confirm button can be simplified for better readability.

Suggested Fix:

Use Object.values(confirmations).includes(false) instead of some:

disabled={
  queryLoading ||
- Object.values(confirmations).some((confirmation) => !confirmation)
+ Object.values(confirmations).includes(false)
}

160-163: Improve Accessibility by Providing Descriptive Messages

The UserNotification component displays a warning message. Ensure that the message provided by t('manage.courseList.courseDeletionMessage') is clear and descriptive for better user experience.

Consider reviewing the translation key for completeness.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between e71423a and 33d143c.

📒 Files selected for processing (19)
  • apps/frontend-manage/src/components/courses/CourseListButton.tsx (0 hunks)
  • apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (1 hunks)
  • apps/frontend-manage/src/pages/courses/index.tsx (6 hunks)
  • packages/graphql/src/graphql/ops/MDeleteCourse.graphql (1 hunks)
  • packages/graphql/src/graphql/ops/QGetCourseSummary.graphql (1 hunks)
  • packages/graphql/src/ops.schema.json (3 hunks)
  • packages/graphql/src/ops.ts (9 hunks)
  • packages/graphql/src/public/client.json (2 hunks)
  • packages/graphql/src/public/schema.graphql (3 hunks)
  • packages/graphql/src/public/server.json (2 hunks)
  • packages/graphql/src/schema/course.ts (1 hunks)
  • packages/graphql/src/schema/mutation.ts (1 hunks)
  • packages/graphql/src/schema/query.ts (2 hunks)
  • packages/graphql/src/services/courses.ts (1 hunks)
  • packages/i18n/messages/de.ts (1 hunks)
  • packages/i18n/messages/en.ts (1 hunks)
  • packages/prisma/src/data/seedTEST.ts (2 hunks)
  • packages/prisma/src/prisma/migrations/20241001202725_course_cascade_deletion/migration.sql (1 hunks)
  • packages/prisma/src/prisma/schema/participant.prisma (2 hunks)
💤 Files with no reviewable changes (1)
  • apps/frontend-manage/src/components/courses/CourseListButton.tsx
🔇 Additional comments (31)
packages/graphql/src/graphql/ops/MDeleteCourse.graphql (1)

1-5: LGTM! The mutation is well-structured and aligns with the PR objectives.

The DeleteCourse mutation is correctly implemented with a clear name, appropriate argument type, and a minimal return value. It fulfills the requirement of adding the ability to delete courses as specified in the PR objectives.

packages/graphql/src/graphql/ops/QGetCourseSummary.graphql (1)

1-10: Verify the resolver implementation.

The query structure looks good, but it's important to ensure that the corresponding resolver getCourseSummary is implemented correctly to match this query.

Let's verify the resolver implementation:

Please ensure that the resolver implementation matches the query structure and returns all the specified fields.

packages/prisma/src/prisma/migrations/20241001202725_course_cascade_deletion/migration.sql (5)

7-11: LGTM: Dropping foreign key constraints

The removal of foreign key constraints for LeaderboardEntry and ParticipantGroup tables is a necessary preparatory step for the subsequent schema modifications. This approach follows best practices for database migrations.


13-14: LGTM: Setting ParticipantGroup.courseId to NOT NULL

This change enforces the requirement that every participant group must be associated with a course, which is crucial for maintaining data integrity when implementing course deletion functionality.

As mentioned earlier, ensure that there are no existing NULL values in the courseId column before applying this migration in production.


1-20: Summary: Migration script effectively supports course deletion functionality

This migration script successfully modifies the database schema to support the PR objective of adding course deletion functionality. It ensures referential integrity by:

  1. Making ParticipantGroup.courseId non-nullable
  2. Implementing cascading deletes for related ParticipantGroup and LeaderboardEntry records

The script is well-structured and follows best practices for database migrations. However, it's crucial to:

  • Verify the absence of NULL values in ParticipantGroup.courseId before applying the migration
  • Implement safeguards against unintended data loss due to cascading deletes
  • Thoroughly test the course deletion functionality in a staging environment before deploying to production

Overall, this migration script provides a solid foundation for the new course deletion feature.


16-20: Approve with caution: Adding cascading foreign key constraints

The new foreign key constraints with CASCADE clauses for both ParticipantGroup and LeaderboardEntry tables effectively support the course deletion functionality. This ensures that related data is automatically cleaned up when a course is deleted.

However, cascading deletes can potentially lead to unintended data loss. Consider implementing the following safeguards:

  1. Add a "soft delete" mechanism for courses to allow for data recovery.
  2. Implement application-level checks to warn users about the implications of course deletion.
  3. Regularly backup your database to allow for data recovery if needed.

To verify the impact of these cascading deletes, you can run the following query to check the number of related entries that would be affected by a course deletion:

#!/bin/bash
echo "
SELECT c.id AS course_id, 
       c.name AS course_name, 
       COUNT(DISTINCT pg.id) AS participant_group_count, 
       COUNT(DISTINCT le.id) AS leaderboard_entry_count
FROM \"Course\" c
LEFT JOIN \"ParticipantGroup\" pg ON c.id = pg.\"courseId\"
LEFT JOIN \"LeaderboardEntry\" le ON c.id = le.\"courseId\"
GROUP BY c.id, c.name
ORDER BY participant_group_count DESC, leaderboard_entry_count DESC
LIMIT 10;
" | psql -d your_database_name

This will help you understand the potential impact of course deletions on related data.


1-6: Verify absence of NULL values in ParticipantGroup.courseId before migration

The warning message indicates a critical change: making the courseId column in the ParticipantGroup table non-nullable. This aligns with the PR objective of course deletion management, ensuring data integrity.

Before applying this migration in production, run the following query to ensure there are no NULL values in the courseId column:

If the count is greater than 0, you'll need to handle these cases before proceeding with the migration.

packages/graphql/src/public/client.json (3)

25-25: LGTM: DeleteCourse entry added correctly.

The addition of the "DeleteCourse" entry aligns with the PR objective of implementing course deletion functionality. The format and structure of the entry are consistent with other entries in the file.


117-117: LGTM: GetCourseSummary entry added correctly.

The addition of the "GetCourseSummary" entry is consistent with the PR objective of implementing functionality to retrieve course summaries. The format and structure of the entry match the existing entries in the file.


Line range hint 1-173: Overall assessment: Changes are minimal, focused, and align with PR objectives.

The modifications to this file are limited to the addition of two new entries: "DeleteCourse" and "GetCourseSummary". These additions are consistent with the PR objectives of implementing course deletion and summary retrieval functionalities. The changes maintain the existing file structure and format, introducing no apparent issues or inconsistencies.

packages/graphql/src/schema/query.ts (2)

13-18: LGTM: Import statement updated correctly

The import statement has been appropriately expanded to include CourseSummary, which is consistent with the new query field being added. This change maintains the existing structure and improves code organization.


Line range hint 1-724: Overall assessment: Changes are well-implemented and align with PR objectives

The modifications to this file, including the new getCourseSummary query field and the updated import statement, are well-implemented and consistent with the existing code structure. These changes enhance the API's functionality and align with the PR objectives of adding the ability to delete courses. The new query field for fetching a course summary could be particularly useful in the course deletion process, providing necessary information before deletion.

packages/graphql/src/public/schema.graphql (1)

212-219: Summary: Changes align well with PR objectives

The additions to the GraphQL schema effectively support the new feature of course deletion and retrieval of course summaries. The new CourseSummary type, deleteCourse mutation, and getCourseSummary query are well-structured and consistent with the existing schema.

These changes align perfectly with the PR objectives mentioned in the summary, particularly:

  1. The ability to delete courses (implemented through the deleteCourse mutation).
  2. Retrieving course summaries (implemented through the getCourseSummary query and CourseSummary type).

The suggested improvements for error handling and additional summary fields would further enhance the robustness and usefulness of these new features.

Also applies to: 768-768, 1077-1077

packages/graphql/src/schema/mutation.ts (2)

Line range hint 1-1401: File structure and consistency maintained

The addition of the deleteCourse mutation has been done in a way that maintains the overall structure and consistency of the file. The mutation is placed appropriately within the user operations section, and it follows the established patterns for mutation definitions in this file.

The clear organization of mutations into sections based on user roles (anonymous, participant, user, user with catalyst, and user owner) is maintained, which contributes to the readability and maintainability of the code.


621-631: New mutation added: deleteCourse

The deleteCourse mutation has been successfully added to the GraphQL schema. It allows users with the USER role to delete a course by providing its ID. The implementation looks correct and follows the established pattern for other mutations in this file.

A few observations and suggestions:

  1. The mutation is properly authenticated with t.withAuth(asUser).
  2. The return type is nullable and of type Course, which is appropriate.
  3. The id argument is correctly defined as a required string.

To ensure completeness, consider the following:

This will help confirm that the deleteCourse function is properly implemented in the CourseService.

packages/graphql/src/public/server.json (1)

Line range hint 25-29: LGTM: New DeleteCourse mutation added

The new DeleteCourse mutation has been successfully added. It takes a course id as input and returns the id of the deleted course, which is consistent with the PR objective and follows the structure of other mutations in the file.

packages/i18n/messages/en.ts (2)

1508-1530: Well-structured and informative course deletion messages.

The newly added keys for course deletion provide clear and detailed information about the consequences of deleting a course. Each message specifies the number of affected elements (like live quizzes, practice quizzes, microlearnings, etc.) and explains what will happen to them. This approach helps users make informed decisions before proceeding with course deletion.


Line range hint 1-1530: Well-structured and comprehensive localization file.

This file demonstrates a well-organized and extensive set of localization strings for the application. The structure is consistent, with clear separation between different sections (shared, pwa, manage, control). The newly added course deletion messages integrate seamlessly with the existing content, maintaining the established patterns and conventions.

The comprehensive nature of this file suggests a strong commitment to internationalization and user experience, providing clear and informative messages across various parts of the application.

packages/i18n/messages/de.ts (1)

1527-1550: New course deletion feature looks good!

The newly added keys for course deletion provide clear and informative messages to the user. The text is well-structured and consistent with the rest of the file's language (German).

packages/graphql/src/ops.ts (7)

840-840: LGTM! deleteCourse mutation added successfully.

The deleteCourse mutation has been correctly added to the list of possible mutations. The return type of Maybe<Course> appropriately indicates that the operation might not always succeed.


1106-1108: LGTM! MutationDeleteCourseArgs type defined correctly.

The MutationDeleteCourseArgs type is well-defined, using a string ID as the input for the deleteCourse mutation. This follows GraphQL best practices and maintains consistency with the existing codebase structure.


1818-1818: LGTM! getCourseSummary query added successfully.

The getCourseSummary query has been correctly added to the list of possible queries. Its return type of Maybe<CourseSummary> appropriately handles cases where a summary might not be available. This query complements the deleteCourse mutation well, providing necessary information for informed deletion decisions.


1933-1935: LGTM! QueryGetCourseSummaryArgs type defined correctly.

The QueryGetCourseSummaryArgs type is well-defined, using a string courseId as the input for the getCourseSummary query. This maintains consistency with the deleteCourse mutation and follows GraphQL best practices.


3365-3370: LGTM! GetCourseSummaryQuery types defined correctly.

The GetCourseSummaryQueryVariables and GetCourseSummaryQuery types are well-defined and consistent with earlier type definitions. The query response includes all fields from the CourseSummary type, providing a comprehensive overview of the course.


3707-3707: LGTM! GraphQL document definitions added correctly.

The GraphQL document definitions for DeleteCourseMutation and GetCourseSummaryQuery have been correctly added. They are consistent with the type definitions and follow the established pattern for other operations in the file.

Also applies to: 3799-3799


Line range hint 250-3799: Summary: Excellent implementation of course deletion functionality

The changes in this file successfully implement the course deletion functionality and related query as outlined in the PR objectives. Key points:

  1. New CourseSummary type provides comprehensive course statistics.
  2. deleteCourse mutation and getCourseSummary query are correctly implemented.
  3. All necessary types and GraphQL document definitions are added consistently.

The code maintains good structure and follows existing patterns in the codebase. Minor suggestions for improvements have been made, but overall, this is a solid implementation of the new feature.

packages/graphql/src/ops.schema.json (1)

Line range hint 2442-16544: Overall, excellent additions to the GraphQL schema.

The changes introduced in this file effectively support the new course deletion feature and enhance course management capabilities. The new CourseSummary type, deleteCourse mutation, and getCourseSummary query are well-structured and logically placed within the schema.

A few minor suggestions have been made to further improve the schema:

  1. Adding a totalComponents field to the CourseSummary type.
  2. Enhancing error handling for the deleteCourse mutation.
  3. Renaming getCourseSummary to courseSummary for consistency.

These changes provide a solid foundation for implementing the course deletion feature as described in the PR objectives.

packages/prisma/src/prisma/schema/participant.prisma (1)

170-171: Verify cascading delete impact on LeaderboardEntry data

The onDelete behavior for the course relation in the LeaderboardEntry model has been updated to Cascade. Deleting a Course will now delete all associated LeaderboardEntry records.

Please verify that:

  • This behavior is intended and does not affect historical data or reports that rely on LeaderboardEntry records.
  • There are no needs to retain LeaderboardEntry data after a Course is deleted for analytics or auditing purposes.

To assess the impact, you can run the following script to identify dependencies:

✅ Verification successful

Cascading delete impact on LeaderboardEntry verified successfully.

No references to reporting, analytics, auditing, or historical data dependencies were found that rely on LeaderboardEntry records. Therefore, the cascading delete behavior is as intended and does not affect necessary data.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all references to `LeaderboardEntry` that may be affected by course deletion.

# Search for usages of `LeaderboardEntry` without a course association.
ast-grep --lang prisma --pattern 'model LeaderboardEntry {
  $$$
  course   Course? @relation(fields: [courseId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  $$$
}'

Length of output: 293


Script:

#!/bin/bash
# Description: Find usages of `LeaderboardEntry` related to course deletions.

# Search for references to `LeaderboardEntry` and `courseId`.
rg 'LeaderboardEntry.*courseId'

Length of output: 932


Script:

#!/bin/bash
# Description: Search for usages of `LeaderboardEntry` outside of migration files to verify dependencies.

# Exclude migration directories and search for `LeaderboardEntry` references
rg 'LeaderboardEntry' --glob '!**/migrations/**'

Length of output: 12256


Script:

#!/bin/bash
# Description: Search for reporting, analytics, or auditing references involving `LeaderboardEntry`.

# Search for keywords related to reporting, analytics, auditing in files that reference `LeaderboardEntry`
rg 'LeaderboardEntry' --glob '!**/migrations/**' | rg -i 'report|analytics|audit|historical'

Length of output: 92

apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (1)

70-88: Ensure Correct Cache Update in deleteCourse Mutation

In the update function of the useMutation, the cache is being read and written to update the userCourses query. Ensure that the cache update correctly reflects the deletion to prevent UI inconsistencies.

You can verify the cache update by inspecting the userCourses after deletion.

packages/prisma/src/data/seedTEST.ts (2)

Line range hint 260-266: Appropriate course association for single participant groups

Adding the course connection to single participant groups ensures that they are correctly linked to the course, which is essential for data integrity and consistent behavior in the application.


Line range hint 356-363: Consistent addition of course association to participant groups

Including the course connection when creating participant groups aligns with the data model and ensures that all groups are properly associated with their respective courses.

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: 6

🧹 Outside diff range and nitpick comments (1)
cypress/cypress/e2e/E-course-workflow.cy.ts (1)

Line range hint 441-667: Split the extensive test into smaller, focused test cases

The test function starting at line 441 includes multiple functionalities: archiving/unarchiving courses, creating courses with live quizzes, practice quizzes, microlearning, and deleting courses. Breaking this test into smaller, focused tests will enhance readability and maintainability. It will also make it easier to identify and debug specific issues.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 33d143c and 885bf7e.

📒 Files selected for processing (2)
  • apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (1 hunks)
  • cypress/cypress/e2e/E-course-workflow.cy.ts (2 hunks)
🔇 Additional comments (4)
apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (3)

1-12: Imports look good and are well-organized.

The necessary dependencies, GraphQL operations, and UI components are correctly imported. The use of a design system (@uzh-bf/design-system) promotes consistency across the application.


264-265: Overall structure and export look good

The component is correctly exported as the default export, which is appropriate for this type of component. The file structure follows React component best practices, with imports at the top, component definitions, and the export at the bottom.


1-265: Summary: Well-implemented component with room for minor improvements

The CourseDeletionModal component is well-structured and implements the required functionality for course deletion with a confirmation mechanism. The code is generally clean and follows React best practices. However, there are a few areas for improvement:

  1. Update the useEffect dependency array to include data?.getCourseSummary.
  2. Move initialConfirmations inside the component to ensure proper state isolation.
  3. Enhance the optimistic response in the deleteCourse mutation to match the shape of the GetUserCoursesDocument query.
  4. Consider using a named interface for the CourseDeletionItem props.

Addressing these points will further improve the component's robustness and maintainability. Overall, good job on implementing this feature!

cypress/cypress/e2e/E-course-workflow.cy.ts (1)

660-667: Verify the state of live quizzes after deleting a course

After deleting the course, the test accesses and edits the live quiz that was associated with the deleted course. Please verify if this is the intended behavior. If the live quiz should no longer be accessible or should be deleted alongside the course, the test should assert that it is no longer available. If it remains accessible but unassociated with any course, ensure this aligns with the application's requirements.

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

🧹 Outside diff range and nitpick comments (1)
cypress/cypress/e2e/E-course-workflow.cy.ts (1)

615-657: Thorough course deletion process with room for improvement

The course deletion process is well-implemented, checking for the existence of different course elements and confirming their deletion. The test also verifies that the course is no longer visible after deletion, which is crucial.

However, to make the test even more robust, consider adding checks to verify that the practice quiz and microlearning session are also no longer accessible after course deletion, similar to how you check for the live quiz in lines 659-667.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 885bf7e and 40ec51b.

📒 Files selected for processing (1)
  • cypress/cypress/e2e/E-course-workflow.cy.ts (2 hunks)
🔇 Additional comments (3)
cypress/cypress/e2e/E-course-workflow.cy.ts (3)

Line range hint 441-476: LGTM: Archiving and unarchiving test case

The changes in this section are minimal and improve readability. The functionality for archiving and unarchiving courses remains intact and well-tested.


477-614: Excellent addition: Comprehensive test for course creation and element association

This new test case significantly enhances the test coverage for course management functionality. It thoroughly tests the creation of a course along with associated elements such as live quizzes, practice quizzes, and microlearning sessions. This ensures that the course creation process and its integration with various learning elements work as expected.


Line range hint 441-667: Excellent enhancements to course management testing

The changes in this file significantly improve the test coverage for course management functionality. The new test case comprehensively covers the creation of a course with various elements (live quiz, practice quiz, and microlearning), as well as the deletion process. This ensures that the entire lifecycle of a course, including its associated elements, is thoroughly tested.

Key improvements:

  1. Comprehensive testing of course creation with different types of quizzes and learning sessions.
  2. Verification of course element associations during creation.
  3. Thorough testing of the course deletion process, including checks for element removal.

These enhancements will greatly contribute to the overall quality and reliability of the course management system. Great job on improving the test suite!

cypress/cypress/e2e/E-course-workflow.cy.ts Show resolved Hide resolved
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

🧹 Outside diff range and nitpick comments (1)
apps/frontend-manage/src/pages/courses/index.tsx (1)

Line range hint 1-256: Consider refactoring for improved maintainability

While the changes in this file are well-implemented, the growing complexity suggests that some refactoring could improve maintainability:

  1. Consider extracting the course list item rendering (including the course button, archive button, and delete button) into a separate component. This would reduce the complexity of the CourseSelectionPage component and improve reusability.

  2. The modal state management (for creation, archiving, and deletion) could potentially be consolidated using a single state object and a generic modal component, reducing repetition.

  3. Consider moving the course creation logic into a custom hook or separate function to keep the component focused on rendering.

These refactoring suggestions aim to improve the overall structure and maintainability of the code as the feature set grows.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 40ec51b and 5bb8d3c.

📒 Files selected for processing (2)
  • apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (1 hunks)
  • apps/frontend-manage/src/pages/courses/index.tsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx
🧰 Additional context used
📓 Learnings (2)
📓 Common learnings
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4282
File: apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx:138-143
Timestamp: 2024-10-01T22:01:50.985Z
Learning: The `deleteCourse` mutation returns only the `id` field.
apps/frontend-manage/src/pages/courses/index.tsx (1)
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4282
File: apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx:138-143
Timestamp: 2024-10-01T22:01:50.985Z
Learning: The `deleteCourse` mutation returns only the `id` field.
🔇 Additional comments (6)
apps/frontend-manage/src/pages/courses/index.tsx (6)

22-22: LGTM: New imports for enhanced functionality

The addition of twMerge and CourseDeletionModal imports are appropriate for the new features being implemented. The twMerge utility will help manage Tailwind classes more efficiently, while CourseDeletionModal is necessary for the new course deletion functionality.

Also applies to: 25-26


41-41: LGTM: New state for deletion modal

The addition of deletionModal state and its setter showDeletionModal is appropriate for managing the visibility of the course deletion modal.


66-87: LGTM: Improved layout and conditional rendering

The changes to the layout and conditional rendering are well-implemented:

  • The adjusted width improves responsiveness.
  • The archive toggle button is now correctly shown only when courses exist.
  • The use of ?? 0 instead of ?? -1 for the default length value is a good improvement, addressing the previous review comment.

These changes enhance both the functionality and clarity of the code.


90-90: LGTM: Enhanced course list structure and deletion functionality

The changes to the course list rendering are well-implemented:

  • The added wrapper div improves the structure of the course list.
  • The course deletion button is correctly implemented with an appropriate click handler, setting up the course for deletion and showing the deletion modal.

These changes enhance both the structure and functionality of the course list.

Also applies to: 127-130


148-153: LGTM: Improved empty state styling

The changes to the empty state rendering are well-implemented:

  • The use of twMerge for dynamic class merging enhances the flexibility of the styling.
  • The condition for applying the md:pr-24 class is consistent with earlier changes, maintaining layout consistency.

These changes improve the responsiveness and consistency of the empty state UI.


174-178: LGTM: Addition of CourseDeletionModal

The CourseDeletionModal is correctly implemented:

  • It receives the necessary props: open, setOpen, and courseId.
  • This addition is consistent with the new course deletion functionality introduced earlier in the component.

This change completes the implementation of the course deletion feature in the UI.

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

🧹 Outside diff range and nitpick comments (10)
apps/frontend-manage/src/components/courses/modals/CourseDeletionItem.tsx (2)

6-17: Component definition and props are well-structured.

The props are clearly defined with appropriate types. The optional data prop for custom attributes is a good practice for testing. Consider using a separate interface for the props to improve readability and reusability.

Consider refactoring the props into a separate interface:

interface CourseDeletionItemProps {
  label: string;
  confirmed: boolean;
  onClick: () => void;
  data?: { cy?: string; test?: string };
}

function CourseDeletionItem({ label, confirmed, onClick, data }: CourseDeletionItemProps) {
  // ... component implementation
}

19-37: Render function is well-implemented with good use of conditional rendering.

The component effectively uses conditional rendering based on the confirmed prop, providing clear visual feedback with icons. The Button component is appropriately used with custom styling.

Consider improving accessibility by adding an aria-label to the FontAwesome icons:

-<FontAwesomeIcon icon={faExclamationCircle} className="text-red-600" />
+<FontAwesomeIcon icon={faExclamationCircle} className="text-red-600" aria-label={t('shared.generic.warning')} />

-<FontAwesomeIcon icon={faCheck} className="text-green-700" />
+<FontAwesomeIcon icon={faCheck} className="text-green-700" aria-label={t('shared.generic.confirmed')} />

Don't forget to add the corresponding translations for these new labels.

apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (3)

50-73: Well-implemented GraphQL query and mutation setup

The GraphQL query and mutation setups are well-implemented, following Apollo Client best practices. The cache update logic in the mutation is particularly well-handled, ensuring the UI stays in sync after a course deletion.

Consider adding error handling to the query and mutation to improve the user experience in case of network issues or other errors. For example:

const { data, loading: queryLoading, error: queryError } = useQuery(GetCourseSummaryDocument, {
  // ... existing options
});

const [deleteCourse, { loading: courseDeleting, error: deleteError }] = useMutation(
  DeleteCourseDocument,
  // ... existing options
);

// Then, use queryError and deleteError to display error messages to the user if needed

93-108: Well-structured Modal setup with proper conditional rendering

The conditional rendering and Modal component setup are well-implemented. Returning null when necessary data is unavailable prevents rendering an incomplete modal.

Consider adding a loading state to improve user experience:

if (queryLoading) {
  return <LoadingSpinner /> // or any loading indicator component
}

if (!courseId || !data?.getCourseSummary) {
  return null
}

// ... rest of the component

This addition would provide visual feedback to users while the course data is being fetched.


109-151: Well-implemented Modal actions with proper state management

The Modal's action buttons are well-implemented with appropriate disabling logic and state resets. The optimistic response in the deleteCourse mutation is correctly structured, which will provide a smooth user experience during course deletion.

Consider adding error handling to improve user feedback:

onClick={async () => {
  try {
    await deleteCourse({
      // ... existing options
    })
    setOpen(false)
    setSelectedCourseId(null)
    setConfirmations({ ...initialConfirmations })
  } catch (error) {
    // Handle error, e.g., show an error message to the user
    console.error('Failed to delete course:', error)
    // You might want to use a toast or alert component here
  }
}}

This addition would provide better feedback to users if the course deletion fails for any reason.

apps/frontend-manage/src/pages/courses/index.tsx (2)

Line range hint 90-134: LGTM: Addition of course deletion functionality

The implementation of the delete button for each course aligns well with the PR objective. The button's styling, positioning, and functionality are appropriate.

Suggestion for improvement:
Consider adding an aria-label to the delete button to enhance accessibility. For example:

 <Button
   className={{
     root: 'flex h-10 w-10 items-center justify-center border border-red-600',
   }}
   onClick={() => {
     setSelectedCourseId(course.id)
     showDeletionModal(true)
   }}
   data={{ cy: `delete-course-${course.name}` }}
+  aria-label={`Delete course ${course.name}`}
 >
   <FontAwesomeIcon icon={faTrashCan} />
 </Button>

This change would improve the experience for users relying on screen readers.


175-180: LGTM: Addition of CourseDeletionModal

The inclusion of the CourseDeletionModal component aligns perfectly with the PR objective of adding course deletion functionality. The props passed to the modal seem appropriate for controlling its visibility and managing the selected course.

Suggestion for improvement:
Consider using more specific prop types for open and setOpen. For example:

open: boolean
setOpen: (open: boolean) => void

This would provide better type safety and make the component's API more clear to other developers.

apps/frontend-manage/src/components/courses/modals/CourseDeletionConfirmations.tsx (3)

7-15: Define a separate interface for the summary prop

Creating a dedicated interface for summary enhances code clarity and allows for reuse if needed elsewhere.

Define the CourseDeletionSummary interface:

interface CourseDeletionSummary {
  numOfLiveQuizzes: number;
  numOfPracticeQuizzes: number;
  numOfMicroLearnings: number;
  numOfGroupActivities: number;
  numOfParticipantGroups: number;
  numOfLeaderboardEntries: number;
}

interface CourseDeletionConfirmationsProps {
  summary: CourseDeletionSummary;
  confirmations: CourseDeletionConfirmationType;
  setConfirmations: Dispatch<SetStateAction<CourseDeletionConfirmationType>>;
}

This change improves readability and maintains consistency in your type definitions.


42-47: Consider toggling confirmation states

Currently, clicking an item sets its confirmation state to true unconditionally. If there's a possibility that a user might want to deselect an option, consider toggling the state instead.

Modify the onClick handlers to toggle the state:

onClick={() => {
  setConfirmations((prev) => ({
    ...prev,
-   disconnectLiveQuizzes: true,
+   disconnectLiveQuizzes: !prev.disconnectLiveQuizzes,
  }));
}}

Apply similar changes to the other onClick handlers if toggling is desirable.

Also applies to: 59-64, 76-81, 93-98, 110-115, 127-132


20-138: Add unit tests for the CourseDeletionConfirmations component

Including unit tests ensures that the component behaves as expected, particularly with various combinations of summary data and user interactions.

Would you like assistance in creating unit tests for this component or opening a GitHub issue to track this task?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 5bb8d3c and f6279f5.

📒 Files selected for processing (5)
  • apps/frontend-manage/src/components/courses/modals/CourseArchiveModal.tsx (4 hunks)
  • apps/frontend-manage/src/components/courses/modals/CourseDeletionConfirmations.tsx (1 hunks)
  • apps/frontend-manage/src/components/courses/modals/CourseDeletionItem.tsx (1 hunks)
  • apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (1 hunks)
  • apps/frontend-manage/src/pages/courses/index.tsx (6 hunks)
🧰 Additional context used
📓 Learnings (3)
apps/frontend-manage/src/components/courses/modals/CourseDeletionItem.tsx (1)
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4282
File: apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx:138-143
Timestamp: 2024-10-01T22:01:50.985Z
Learning: The `deleteCourse` mutation returns only the `id` field.
apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (1)
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4282
File: apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx:138-143
Timestamp: 2024-10-01T22:01:50.985Z
Learning: The `deleteCourse` mutation returns only the `id` field.
apps/frontend-manage/src/pages/courses/index.tsx (1)
Learnt from: sjschlapbach
PR: uzh-bf/klicker-uzh#4282
File: apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx:138-143
Timestamp: 2024-10-01T22:01:50.985Z
Learning: The `deleteCourse` mutation returns only the `id` field.
🔇 Additional comments (16)
apps/frontend-manage/src/components/courses/modals/CourseDeletionItem.tsx (3)

1-4: Imports look good and are well-organized.

The necessary dependencies are imported for icons, UI components, and internationalization. There are no unused imports, and they are logically grouped.


40-40: Export statement is correct.

The component is properly exported as the default export, following common React patterns.


1-40: Overall, the CourseDeletionItem component is well-implemented.

The component effectively handles the course deletion confirmation UI, with good use of conditional rendering and internationalization. The code is clean, well-structured, and follows React best practices. The minor suggestions for improving prop types and accessibility will further enhance the component's quality and maintainability.

apps/frontend-manage/src/components/courses/modals/CourseArchiveModal.tsx (5)

13-13: LGTM: New prop aligns with PR objectives

The addition of the setSelectedCourseId prop enhances the component's ability to manage the selected course state, which aligns well with the PR's objective of improving course management functionality.


21-21: LGTM: Function signature updated consistently

The CourseArchiveModal function signature has been correctly updated to include the new setSelectedCourseId prop, maintaining consistency with the interface definition.


37-40: LGTM: Improved state management on modal close

The modification to the onClose handler now resets the selected course ID when the modal is closed. This enhancement:

  1. Improves state management by preventing stale selections.
  2. Aligns with the PR's goal of enhancing user awareness and preventing accidental actions.
  3. Ensures a clean slate for the next interaction with the modal.

63-63: LGTM: Consistent state reset in action handlers

The changes to both the primary (confirm) and secondary (cancel) action button handlers now include resetting the selected course ID. This update:

  1. Ensures consistent behavior regardless of how the modal is closed.
  2. Reinforces the improved state management implemented in the onClose handler.
  3. Aligns with the PR's goal of enhancing user interaction and preventing unintended actions.

Also applies to: 73-76


Line range hint 1-93: Overall assessment: Well-implemented changes enhancing course management

The modifications to CourseArchiveModal.tsx have been implemented consistently and thoroughly. The new setSelectedCourseId prop is utilized effectively throughout the component, ensuring proper state management when interacting with the modal. These changes align well with the PR's objectives of enhancing course management functionality and improving user interaction.

Key improvements:

  1. Addition of setSelectedCourseId prop to the interface and component.
  2. Consistent implementation in modal close and action button handlers.
  3. Enhanced state management, preventing potential issues with stale selections.

The changes contribute positively to the overall user experience and course management functionality.

apps/frontend-manage/src/components/courses/modals/CourseDeletionModal.tsx (3)

1-26: Well-structured imports and interface definitions

The imports and interface definitions are well-organized and provide clear type information for the component. This enhances code readability and maintainability.


76-91: Efficient useEffect implementation for updating confirmations

The useEffect hook is well-implemented, efficiently updating the confirmations state based on the fetched course summary. It correctly skips confirmations for elements that are not present, which improves the user experience.

The dependency array is correctly set up, ensuring the effect runs when either the courseId or the course summary data changes.


152-162: Well-structured Modal content and proper component export

The Modal's content is well-organized, using a separate CourseDeletionConfirmations component. This separation of concerns enhances code readability and maintainability. The component is correctly exported as default, allowing for easy import and use in other parts of the application.

The props passed to CourseDeletionConfirmations (summary, confirmations, and setConfirmations) provide all necessary data and callbacks for its functionality.

apps/frontend-manage/src/pages/courses/index.tsx (5)

22-22: LGTM: New imports for enhanced functionality

The addition of twMerge and CourseDeletionModal imports aligns well with the PR objectives. These imports support the new course deletion feature and improve dynamic styling capabilities.

Also applies to: 25-26


41-41: LGTM: State management for deletion modal

The addition of the deletionModal state variable and its setter showDeletionModal is appropriate for managing the visibility of the course deletion modal. This change aligns with the PR objective of implementing course deletion functionality.


66-87: LGTM: Improved layout and conditional rendering

The changes in this section improve the layout and conditional rendering:

  1. The adjusted width enhances responsiveness.
  2. The archive toggle button is now only shown when courses exist, improving UI clarity.
  3. Using 0 as the default value for dataCourses?.userCourses?.length is more intuitive than -1 for array lengths, addressing a previous review comment.

These improvements enhance the overall user experience and code readability.


148-153: LGTM: Improved styling for "no courses" notification

The use of twMerge for conditional class application is a good practice. It allows for dynamic styling based on the presence of courses, improving the overall layout and responsiveness of the notification. This change enhances the user interface and maintains consistency with the rest of the component.


43-45: ⚠️ Potential issue

Consider re-implementing error handling for the query

While simplifying the query improves readability, removing error handling might lead to unhandled errors. Consider re-implementing error handling as suggested in a previous review comment:

const { loading: loadingCourses, data: dataCourses, error } = useQuery(
  GetUserCoursesDocument
)

// Add error handling in the component body
if (error) {
  // Handle the error, e.g., show an error message to the user
  return <ErrorComponent message={error.message} />
}

This ensures robustness and improves user experience in case of query failures.

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

🧹 Outside diff range and nitpick comments (13)
packages/graphql/src/public/schema.graphql (1)

768-768: LGTM: deleteCourse mutation added

The deleteCourse mutation is correctly implemented, following the schema's naming conventions. Returning the deleted Course object is a good practice for confirmation.

Consider adding explicit null handling to cover cases where the course might not exist:

deleteCourse(id: String!): Course

Could be changed to:

deleteCourse(id: String!): DeleteCourseResult

Where DeleteCourseResult is a new type that includes both the deleted course and a success flag:

type DeleteCourseResult {
  success: Boolean!
  course: Course
}

This would provide more explicit information about the operation's outcome.

packages/graphql/src/public/server.json (3)

Line range hint 1-1: Overall structure assessment

The file contains a comprehensive collection of GraphQL operations (queries, mutations, and subscriptions) for various functionalities of the application. The use of a JSON structure with hashed keys is a common approach for organizing and caching GraphQL operations on the client-side.

However, consider the following suggestions for improvement:

  1. Documentation: Add comments or documentation to explain the purpose of this file and how it's used in the application.
  2. Grouping: Consider grouping related operations (e.g., by feature or entity) to improve maintainability.
  3. Versioning: Implement a versioning system for the GraphQL schema to manage changes over time.

Line range hint 84-84: Enhance security for participant login

The LoginParticipant mutation provides basic login functionality. To improve security, consider implementing the following enhancements:

  1. Rate limiting: Implement rate limiting to prevent brute-force attacks.
  2. Multi-factor authentication (MFA): Offer an option for users to enable MFA for added security.
  3. Password complexity: Ensure that the system enforces strong password policies.
  4. Account lockout: Implement temporary account lockouts after a certain number of failed login attempts.
  5. Secure token generation: Ensure that the authentication tokens are generated securely and have appropriate expiration times.
  6. HTTPS: Ensure all authentication requests are made over HTTPS to prevent man-in-the-middle attacks.

Would you like assistance in implementing these security enhancements?


Line range hint 1-1: Summary of review findings and suggestions

Overall, the GraphQL operations defined in this file provide a comprehensive set of functionalities for the application. However, there are several areas where improvements can be made:

  1. Documentation: Add comments or documentation to explain the purpose and usage of this file.
  2. Organization: Consider grouping related operations to improve maintainability.
  3. Security: Enhance security measures for critical operations like login and data deletion.
  4. Performance: Optimize queries that fetch large amounts of data, implementing techniques like pagination and lazy loading.
  5. Error handling: Ensure proper error handling is implemented across all operations.
  6. Consistency: Review naming conventions and ensure consistency across all operations.

Implementing these suggestions will improve the overall quality, security, and performance of the GraphQL API.

Consider using GraphQL Code Generator (https://www.graphql-code-generator.com/) to automatically generate TypeScript types and React hooks from your GraphQL schema. This will improve type safety and developer experience when working with these GraphQL operations.

packages/i18n/messages/de.ts (4)

1528-1551: LGTM! Comprehensive coverage of course deletion process.

The new keys provide a thorough set of messages for the course deletion process, covering various scenarios and content types. This addition will greatly improve the user experience during course deletion.

For consistency, consider using the plural form in all messages, e.g., change "Live-Quizzes" to "Live-Quizze" in the disconnectLiveQuizzes message to match the style of other messages.


Line range hint 1-1527: Consider using a more robust rich text formatting approach.

The localization file is well-structured and comprehensive. However, the use of HTML-like tags (e.g., , ) in some messages might cause issues if the localization system or UI components don't properly handle these tags.

Consider using a more robust rich text formatting approach, such as Markdown or a custom syntax that can be safely parsed and rendered by your UI components. This would improve consistency and reduce the risk of rendering issues across different platforms or frameworks.

Example using a custom syntax:

- joinLeaderboardCourse: 'Treten Sie dem Leaderboard für <b>{name}</b> bei',
+ joinLeaderboardCourse: 'Treten Sie dem Leaderboard für *{name}* bei',

Where *text* could represent bold text in your custom parser.


Line range hint 1-1551: Implement proper pluralization for number-related messages.

The localization file handles most internationalization aspects well. However, some number-related messages might benefit from proper pluralization to ensure grammatical correctness in all cases.

Implement a pluralization system for messages that involve numbers. This will allow for grammatically correct sentences regardless of the count. For example:

- nParticipants: '{number} Teilnehmende',
+ nParticipants: {
+   '0': 'Keine Teilnehmenden',
+   'one': '{number} Teilnehmender',
+   'other': '{number} Teilnehmende'
+ },

This approach allows for different translations based on the count, ensuring grammatical correctness in all cases.


Line range hint 1-1551: Improve file maintainability with additional comments and consistent naming.

The localization file is comprehensive but quite large. To improve long-term maintainability, consider the following suggestions:

  1. Add more comments to clearly separate different sections of the file.
  2. Ensure consistent naming conventions across all keys.
  3. Consider splitting the file into smaller, more manageable chunks based on functionality or app sections.

Example of adding comments and ensuring consistent naming:

// Authentication related messages
auth: {
  // ... existing keys ...
},

// Course management messages
courseManagement: {
  // ... existing keys ...
  createCourse: 'Kurs erstellen',
  editCourse: 'Kurs bearbeiten',
  // ... more keys ...
},

// ... other sections ...

This structure will make it easier to locate and maintain specific sections of the localization file.

packages/graphql/src/ops.ts (4)

840-840: LGTM: deleteCourse mutation added

The deleteCourse mutation has been successfully added, which aligns with the PR objective of implementing course deletion functionality. The return type Maybe<Course> is appropriate, allowing for potential failure scenarios.

Consider adding a specific error type for course deletion failures to provide more detailed error information to the client. This could help in distinguishing between different failure scenarios (e.g., course not found, user not authorized, etc.).


1832-1832: LGTM: getCourseSummary query added

The getCourseSummary query has been successfully added, complementing the new CourseSummary type. The return type Maybe<CourseSummary> is appropriate, allowing for scenarios where a summary might not be available.

Consider adding a null reason or error field to the CourseSummary type. This would allow you to provide more context when a summary is not available (e.g., course not found, insufficient data, etc.), enhancing the API's usability.


2641-2646: LGTM: DeleteCourseMutation types correctly defined

The DeleteCourseMutationVariables and DeleteCourseMutation types are correctly defined. The variables include the necessary id field, and the mutation returns the id of the deleted course.

Consider expanding the return type of DeleteCourseMutation to include more fields from the deleted course. This could provide additional confirmation or context to the client about what was deleted, such as the course name or other identifying information. For example:

export type DeleteCourseMutation = { __typename?: 'Mutation', deleteCourse?: { __typename?: 'Course', id: string, name: string, displayName: string } | null };

Line range hint 250-3831: Summary: Comprehensive implementation of course deletion and summary functionality

The changes in this file successfully implement the new course deletion feature and add course summary capabilities. Key points:

  1. A new CourseSummary type provides comprehensive course statistics.
  2. The deleteCourse mutation and its associated types enable course deletion.
  3. The getCourseSummary query allows retrieval of course statistics.
  4. All necessary GraphQL types, queries, mutations, and document definitions are properly implemented.

These changes align well with the PR objectives and provide a solid foundation for the new features. The code is well-structured and consistent with GraphQL best practices.

As the system grows, consider implementing a caching strategy for course summaries to improve performance, especially if these summaries are frequently accessed or computationally expensive to generate.

packages/graphql/src/ops.schema.json (1)

2442-2548: LGTM! Consider adding a description to the CourseSummary type.

The new CourseSummary type is well-structured and provides a comprehensive overview of course components. The use of Non-Null Int for all fields ensures that these counts will always be available, which is good for data consistency.

Consider adding a description to the CourseSummary type to provide context for its usage. For example:

 {
   "kind": "OBJECT",
   "name": "CourseSummary",
-  "description": null,
+  "description": "Provides a summary of course components and related items",
   "fields": [
     // ... existing fields
   ],

This description will help other developers understand the purpose of this type at a glance.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f6279f5 and b02b5d4.

📒 Files selected for processing (8)
  • packages/graphql/src/ops.schema.json (3 hunks)
  • packages/graphql/src/ops.ts (9 hunks)
  • packages/graphql/src/public/client.json (2 hunks)
  • packages/graphql/src/public/schema.graphql (3 hunks)
  • packages/graphql/src/public/server.json (2 hunks)
  • packages/graphql/src/schema/mutation.ts (1 hunks)
  • packages/i18n/messages/de.ts (1 hunks)
  • packages/i18n/messages/en.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/graphql/src/public/client.json
  • packages/graphql/src/schema/mutation.ts
🔇 Additional comments (14)
packages/graphql/src/public/schema.graphql (3)

212-219: LGTM: Well-structured CourseSummary type

The new CourseSummary type is well-defined with consistent naming conventions and non-nullable fields. This structure ensures data integrity and provides a clear overview of various course-related metrics.


1079-1079: LGTM: getCourseSummary query added

The getCourseSummary query is well-implemented, following the schema's naming conventions and correctly utilizing the new CourseSummary type. The non-nullable courseId input ensures that valid requests are made.


Line range hint 212-1079: Summary: Changes align well with PR objectives

The additions to the GraphQL schema effectively support the new feature for course deletion and summary retrieval. The new CourseSummary type, deleteCourse mutation, and getCourseSummary query are well-structured and consistent with existing schema conventions. These changes provide the necessary backend support for the frontend implementation of course deletion functionality, as outlined in the PR objectives.

Consider implementing the suggested refinement for null handling in the deleteCourse mutation to enhance error handling and provide more explicit operation outcomes.

packages/i18n/messages/en.ts (3)

1509-1531: New course deletion feature added successfully.

The new keys added to the courseList object provide clear and informative messages for the course deletion process. They cover various aspects such as:

  • Confirmation for course deletion
  • Handling of associated elements (live quizzes, practice quizzes, microlearnings, group activities, participant groups, and leaderboard entries)
  • Informative messages when there are no elements to delete

This addition enhances the user experience by providing detailed information about the consequences of course deletion.


Line range hint 1-1531: File structure and consistency maintained.

The entire file demonstrates a well-organized structure for localization strings, covering various features of the KlickerUZH application. Key observations:

  1. Clear separation between different sections (pwa, manage, control).
  2. Consistent naming conventions throughout the file.
  3. Comprehensive coverage of features and scenarios.
  4. New additions (course deletion feature) integrate seamlessly with the existing structure.

The file maintains high quality and consistency, which will facilitate easy maintenance and future additions.


1509-1531: Verify course deletion functionality implementation.

While the localization strings for the new course deletion feature have been added successfully, it's important to ensure that the corresponding functionality is properly implemented in the application logic. Consider the following steps:

  1. Verify that the course deletion process in the application uses these new localization keys.
  2. Ensure that the deletion process handles all the mentioned elements (live quizzes, practice quizzes, microlearnings, etc.) as described in the messages.
  3. Test the course deletion feature thoroughly to confirm that it behaves as expected and displays the correct messages.

To assist in this verification, you can run the following script to check for the implementation of the course deletion functionality:

✅ Verification successful

Course deletion functionality is properly implemented.

The new localization keys are appropriately used across the codebase, and the application handles course deletion and associated elements as described.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash

# Search for files that might contain the course deletion logic
echo "Searching for potential course deletion implementation:"
rg --type typescript --type javascript -i "delete.*course" -g '!*.test.{ts,js}' -g '!**/node_modules/**'

# Search for usage of the new localization keys
echo "Searching for usage of new localization keys:"
rg --type typescript --type javascript "deleteCourse|courseDeletionMessage|disconnectLiveQuizzes|deletePracticeQuizzes|deleteMicroLearnings|deleteGroupActivities|deleteParticipantGroups|deleteLeaderboardEntries" -g '!*.test.{ts,js}' -g '!**/node_modules/**'

Length of output: 658


Script:

#!/bin/bash

# Search for files that might contain the course deletion logic
echo "Searching for potential course deletion implementation:"
rg --type ts --type js -i "delete.*course" -g '!*.test.{ts,js}' -g '!**/node_modules/**'

# Search for usage of the new localization keys
echo "Searching for usage of new localization keys:"
rg --type ts --type js "deleteCourse|courseDeletionMessage|disconnectLiveQuizzes|deletePracticeQuizzes|deleteMicroLearnings|deleteGroupActivities|deleteParticipantGroups|deleteLeaderboardEntries" -g '!*.test.{ts,js}' -g '!**/node_modules/**'

Length of output: 12149

packages/graphql/src/ops.ts (5)

250-258: LGTM: New CourseSummary type adds valuable course statistics

The new CourseSummary type is a well-structured addition that provides comprehensive statistics for a course. It includes counts for various course components such as group activities, leaderboard entries, quizzes, and participant groups. This type will be useful for generating course overviews and analytics.


1108-1110: LGTM: MutationDeleteCourseArgs correctly defined

The MutationDeleteCourseArgs type is correctly defined with a single id field of type String. This is consistent with GraphQL best practices and provides the necessary input for the deleteCourse mutation.


1947-1949: LGTM: QueryGetCourseSummaryArgs correctly defined

The QueryGetCourseSummaryArgs type is properly defined with a single courseId field of type String. This is consistent with GraphQL best practices and provides the necessary input for the getCourseSummary query.


3395-3400: LGTM: GetCourseSummary query types correctly defined

The GetCourseSummaryQueryVariables and GetCourseSummaryQuery types are well-defined. The query variables include the necessary courseId field, and the query returns a comprehensive CourseSummary object with all the relevant numerical fields. This structure will provide clients with a complete overview of the course statistics.


3737-3737: LGTM: GraphQL document definitions added

The GraphQL document definitions for DeleteCourseDocument and GetCourseSummaryDocument have been correctly added. These definitions correspond to the types and operations defined earlier and will enable proper execution of these operations in GraphQL clients.

Also applies to: 3831-3831

packages/graphql/src/ops.schema.json (3)

Line range hint 2442-16634: Overall, excellent additions to the GraphQL schema!

The new CourseSummary type, deleteCourse mutation, and getCourseSummary query are well-designed and align perfectly with the PR objectives. They provide a solid foundation for the new course deletion feature and enhanced course management capabilities.

A few minor suggestions have been made to improve documentation and error handling, which would further enhance the usability and robustness of these new schema elements.

Great job on maintaining consistency with the existing schema structure and following GraphQL best practices!


16606-16634: LGTM! Consider adding a description and caching strategy.

The getCourseSummary query is well-structured and complements the new CourseSummary type. It provides an efficient way for clients to fetch summary data for a course.

Consider the following improvements:

  1. Add a description to the query for better documentation:
 {
   "name": "getCourseSummary",
-  "description": null,
+  "description": "Retrieves a summary of course components and related items for a given course",
   "args": [
     // ... existing args
   ],
  1. Consider implementing a caching strategy for this query, as the summary data might not change frequently. This could improve performance for frequently accessed courses.

To ensure this query is properly integrated, let's check for its usage:

#!/bin/bash
# Description: Check for the usage of getCourseSummary query

# Test: Search for getCourseSummary query usage
rg --type typescript --type javascript 'query.*getCourseSummary.*\(' -g '!**/ops.schema.json'

9825-9853: LGTM! Consider adding a description and error handling.

The deleteCourse mutation is well-structured and aligns with the PR objective. Good job on returning the deleted Course object, as it allows the client to confirm the deletion or use the data if needed.

Consider the following improvements:

  1. Add a description to the mutation for better documentation:
 {
   "name": "deleteCourse",
-  "description": null,
+  "description": "Deletes a course and returns the deleted course data",
   "args": [
     // ... existing args
   ],
  1. Consider adding error handling. For example, you might want to return a union type that includes both the successful deletion result and potential error types. This would allow for more granular error handling on the client side.

To ensure this mutation is properly integrated, let's check for its usage:

packages/graphql/src/public/server.json Outdated Show resolved Hide resolved
packages/graphql/src/public/server.json Show resolved Hide resolved
@rschlaefli
Copy link
Member

rschlaefli commented Oct 3, 2024

Looks very good, and I love the way we have to confirm the dangerous parts!

Some small UI/UX improvements I found when testing:

  • In the messages list where we need to confirm each deletion, we should add another first or last message to confirm that describes the impact for participants (e.g., there are currently 6 participants in your course, they will lose access to the course and all associated materials as well as all the points they might have collected, (they will keep collected XP), or something like that)
  • Change the coloring of the icon on messages like "no leaderboard entries" to grey so the real important messages are clearly distinguishable
  • Add a tooltip on the disabled archive button to explain why it cannot be archived (I wasn't sure why it was disabled initially, but probably because of end date after today)
  • Maybe we could reuse the toggle from the question pool to toggle the display archive (instead of the button that changes text)

Copy link

sonarqubecloud bot commented Oct 3, 2024

Quality Gate Failed Quality Gate failed

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

See analysis details on SonarCloud

@sjschlapbach sjschlapbach merged commit 8d6cfa0 into v3 Oct 3, 2024
9 of 13 checks passed
@sjschlapbach sjschlapbach deleted the course-deletion branch October 3, 2024 11:08
Copy link

cypress bot commented Oct 3, 2024

klicker-uzh    Run #3098

Run Properties:  status check passed Passed #3098  •  git commit 8d6cfa0376: feat: add possibility to delete courses (#4282)
Project klicker-uzh
Branch Review v3
Run status status check passed Passed #3098
Run duration 09m 16s
Commit git commit 8d6cfa0376: feat: add possibility to delete courses (#4282)
Committer Julius Schlapbach
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 1
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 47
View all changes introduced in this branch ↗︎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants