Skip to content

Migrate common UI components to packages/ui #3049

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

Merged
merged 23 commits into from
Jun 9, 2025
Merged

Conversation

VNOsST
Copy link
Collaborator

@VNOsST VNOsST commented Jun 8, 2025

Summary by CodeRabbit

  • New Features

    • Added a dedicated header component for course details, providing improved display of course information.
    • Introduced a toggle to switch between card and table views for course listings.
    • Added new user-facing messages for scenarios such as no courses found or missing descriptions, with expanded localization support in English and Vietnamese.
  • Improvements

    • Enhanced consistency and maintainability by centralizing and updating UI components and localization keys.
    • Improved pagination and search parameter handling in course listings.
    • Updated fallback messages and descriptions for course cards.
  • Bug Fixes

    • Fixed inconsistencies in localization key usage for course-related messages.
  • Refactor

    • Replaced many local UI components with shared, centralized components from the UI library for better maintainability and consistency.
    • Updated import paths and export types for several UI and form components.
    • Moved and reorganized utility functions for path handling into a shared utilities package.
  • Chores

    • Added new dependencies to the UI package for enhanced animation and PDF handling capabilities.
    • Removed redundant or unused local files and components, streamlining the codebase.
    • Improved and expanded localization resources for both English and Vietnamese.

Copy link
Contributor

coderabbitai bot commented Jun 8, 2025

Walkthrough

This update centralizes and refactors UI components for course and module management by replacing local implementations with shared package imports, restructures localization keys, and introduces utility and data table enhancements. Several redundant or local files are deleted, and new or updated components and utilities are provided from shared packages, improving modularity and consistency across the codebase.

Changes

Files/Groups Change Summary
apps/upskii/messages/en.json, apps/upskii/messages/vi.json Localization keys for "no_courses_found" and "no_description_provided" moved into "ws-courses" section and redundant top-level entries removed.
apps/web/messages/en.json, apps/web/messages/vi.json Added new localization keys for course management and sidebar prompts in "common" and "ws-courses" sections.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/columns.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/columns.tsx Changed import path for WorkspaceCourseModuleRowActions to shared UI package.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/layout.tsx Replaced custom header logic with new CourseHeader component; removed related imports and translation logic.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/content/page.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx Updated import for ModuleContentEditor to shared package and refactored content fetching logic.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/layout.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/layout.tsx Changed imports for LinkButton and ModuleToggles to shared package and centralized resource path construction.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/page.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx Changed component imports to shared package; updated module content section to use RichTextEditor in a scrollable area; introduced accordion for sections.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/resources/page.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/page.tsx Changed imports for resource management components to shared package.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/toggles.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/delete-resource.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/file-display.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/pdf-viewer.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/delete-link.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/form.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/row-actions.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/form.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/form.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/link-button.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/section.tsx Deleted local implementations of toggles, resource/file/YouTube link management, row actions, and form components in favor of shared package versions.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/page.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/page.tsx Changed import for CourseModuleForm to shared package.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/columns.tsx Changed import for WorkspaceCourseRowActions to shared UI package.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/page.tsx Changed imports for course management UI components to shared package; added view toggle and card/table view logic in web app.
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/columns.tsx, apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/new-actions.tsx, apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/page.tsx, apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/row-actions.tsx, apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/table.tsx, apps/web/src/app/[locale]/(dashboard)/[wsId]/drive/form.tsx Deleted local drive/storage object management components and forms.
apps/web/src/app/[locale]/(dashboard)/[wsId]/drive/new-actions.tsx Changed import for storage object forms to shared package.
apps/web/src/app/[locale]/(dashboard)/[wsId]/education/quiz-sets/[setId]/layout.tsx Changed import for LinkButton to shared package.
apps/web/src/components/custom-data-table.tsx, apps/upskii/src/components/custom-data-table.tsx Removed local CustomDataTable implementation; re-exported from shared package.
apps/upskii/src/utils/path-helper.ts, packages/utils/src/path-helper.ts Deleted local path helper; introduced new shared joinPath and popPath utility functions.
packages/ui/package.json Added framer-motion and react-pdf as dependencies.
packages/ui/src/components/ui/custom/education/courses/course-card-view.tsx Updated translation keys for fallback text.
packages/ui/src/components/ui/custom/education/courses/course-form.tsx, packages/ui/src/components/ui/custom/education/modules/course-module-form.tsx, packages/ui/src/components/ui/custom/education/modules/module-toggle.tsx, packages/ui/src/components/ui/custom/education/modules/resources/delete-resource.tsx, packages/ui/src/components/ui/custom/education/modules/resources/file-display.tsx, packages/ui/src/components/ui/text-editor/content-editor.tsx Changed export type from default to named export for several components.
packages/ui/src/components/ui/custom/education/courses/course-header.tsx Added new CourseHeader component.
packages/ui/src/components/ui/custom/education/courses/course-row-actions.tsx, packages/ui/src/components/ui/custom/education/modules/course-module-row-actions.tsx, packages/ui/src/components/ui/custom/education/modules/resources/file-upload-form.tsx Updated imports to use shared/named components and updated utility import path.
packages/ui/src/components/ui/custom/tables/custom-data-table.tsx Added new CustomDataTable component with search and pagination integration.
packages/ui/src/hooks/useSearchParams.tsx Added new useSearchParams hook for enhanced URL parameter management.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AppPage
    participant SharedUI
    participant Utils

    User->>AppPage: Navigates to course/module page
    AppPage->>SharedUI: Renders shared CourseHeader, CourseForm, ModuleForm, etc.
    AppPage->>SharedUI: Uses shared CustomDataTable for listing
    AppPage->>SharedUI: Uses shared resource/file management components
    AppPage->>Utils: Calls joinPath/popPath for path handling
    SharedUI->>Utils: Uses joinPath/popPath if needed
    AppPage->>SharedUI: Uses ViewToggle to switch between card/table view
    User->>AppPage: Interacts with UI (edit, delete, upload, etc.)
    AppPage->>SharedUI: Triggers modals/actions via shared components
Loading

Possibly related PRs

Suggested labels

upskii

Suggested reviewers

  • vhpx

Poem

In the garden of code, where the components grow,
Rabbits hop and refactor, letting old files go.
Shared UI blooms, translations align,
Paths join neatly, everything’s fine!
With every hop, the system feels lighter—
Here’s to modular code, and a platform much brighter!

🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx

Oops! Something went wrong! :(

ESLint: 9.28.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@typescript-eslint/parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:799:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:723:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:706:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:307:38)
at #link (node:internal/modules/esm/module_job:170:49)

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/columns.tsx

Oops! Something went wrong! :(

ESLint: 9.28.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@typescript-eslint/parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:799:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:723:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:706:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:307:38)
at #link (node:internal/modules/esm/module_job:170:49)

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/content/page.tsx

Oops! Something went wrong! :(

ESLint: 9.28.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@typescript-eslint/parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:799:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:723:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:706:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:307:38)
at #link (node:internal/modules/esm/module_job:170:49)

  • 35 others
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link

graphite-app bot commented Jun 8, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

Copy link

codecov bot commented Jun 8, 2025

Codecov Report

Attention: Patch coverage is 0.70588% with 422 lines in your changes missing coverage. Please review.

Project coverage is 0.85%. Comparing base (c1de4e8) to head (194dbf4).
Report is 24 commits behind head on main.

Files with missing lines Patch % Lines
packages/ui/src/hooks/useSearchParams.tsx 0.94% 105 Missing ⚠️
[...ale]/(dashboard)/[wsId]/education/courses/page.tsx](https://app.codecov.io/gh/tutur3u/platform/pull/3049?src=pr&el=tree&filepath=apps%2Fweb%2Fsrc%2Fapp%2F%5Blocale%5D%2F%28dashboard%29%2F%5BwsId%5D%2Feducation%2Fcourses%2Fpage.tsx&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tutur3u#diff-YXBwcy93ZWIvc3JjL2FwcC9bbG9jYWxlXS8oZGFzaGJvYXJkKS9bd3NJZF0vZWR1Y2F0aW9uL2NvdXJzZXMvcGFnZS50c3g=) 0.00% 75 Missing ⚠️
packages/utils/src/path-helper.ts 0.00% 59 Missing and 1 partial ⚠️
.../components/ui/custom/tables/custom-data-table.tsx 2.32% 42 Missing ⚠️
...ses/[courseId]/modules/[moduleId]/content/page.tsx 0.00% 32 Missing and 1 partial ⚠️
...ents/ui/custom/education/courses/course-header.tsx 3.22% 30 Missing ⚠️
...ion/courses/[courseId]/modules/[moduleId]/page.tsx 0.00% 13 Missing ⚠️
...ustom/education/modules/resources/file-display.tsx 0.00% 5 Missing ⚠️
...c/app/[locale]/(dashboard)/[wsId]/courses/page.tsx 0.00% 4 Missing ⚠️
.../[locale]/(dashboard)/[wsId]/drive/new-actions.tsx 0.00% 4 Missing ⚠️
... and 28 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3049      +/-   ##
==========================================
+ Coverage    0.84%    0.85%   +0.01%     
==========================================
  Files        2469     2453      -16     
  Lines      301703   299212    -2491     
  Branches     2970     2954      -16     
==========================================
+ Hits         2546     2565      +19     
+ Misses     297019   294544    -2475     
+ Partials     2138     2103      -35     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@vhpx vhpx added the enhancement New feature or request label Jun 8, 2025
@VNOsST VNOsST marked this pull request as ready for review June 9, 2025 05:25
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🔭 Outside diff range comments (2)
packages/ui/src/components/ui/custom/education/modules/module-toggle.tsx (1)

44-49: ⚠️ Potential issue

Initialize loading state and add user feedback on errors.

handlePublishedChange never calls setLoading(true), so loading stays false and the checkbox isn’t disabled during the update. Add setLoading(true) before the Supabase call and wrap in a try/catch to display a toast (via @tuturuuu/ui/hooks/use-toast) on failure.

apps/upskii/messages/en.json (1)

1-4143: 💡 Verification agent

🧩 Analysis chain

Verify duplicate localization entries have been removed at the root.

The PR summary notes that top-level no_courses_found and no_description_provided entries were removed. Please ensure no stray definitions remain outside ws-courses:


🏁 Script executed:

#!/bin/bash
# Search for duplicate localization keys in this file
rg -n '"no_courses_found"' apps/upskii/messages/en.json
rg -n '"no_description_provided"' apps/upskii/messages/en.json

Length of output: 233


Action Required: Remove stray top-level no_courses_found and no_description_provided entries

The PR summary indicated these keys were removed, but they still exist at the root of apps/upskii/messages/en.json:

  • Line 3528: "no_courses_found": "No courses found",
  • Line 3529: "no_description_provided": "No description provided"

Please delete these two lines so that those messages only exist under ws-courses.

🧹 Nitpick comments (16)
packages/ui/src/components/ui/custom/education/modules/module-toggle.tsx (3)

8-18: Remove unused isPublic prop and commented code.

The isPublic prop remains in the prop type but isn’t destructured or used, and its handler is fully commented out. To reduce confusion and dead code, either remove isPublic from the props type or re-enable the public toggle logic if it's still required.


26-42: Clean up or re-enable the public toggle handler.

All of the handlePublicChange code is commented out. If the public-toggle feature is deprecated, drop these lines entirely; otherwise, re-enable and wire it up with its own checkbox.


76-80: Use unique IDs for accessibility.

The hard-coded id="isPublished" may collide if multiple toggles are rendered. Consider using a template literal, e.g. id={isPublished-${moduleId}}, to guarantee uniqueness.

packages/ui/src/components/ui/custom/education/modules/resources/delete-resource.tsx (1)

9-28: Avoid parameter shadowing and improve user feedback.

The inner deleteResource(path: string) parameter shadows the outer path prop—rename it (e.g., filePath) for clarity. Also, surface success/error to the user via a toast (use-toast) instead of silently refreshing or just resetting loading.

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/page.tsx (1)

10-12: Verify shared component imports and add coverage.

The local CourseSection, FileDisplay, and YoutubeEmbed imports have been replaced with package imports. Confirm these paths match the UI library’s exports, and consider adding UI/integration tests to cover these new imports (static analysis flagged them as uncovered).

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 10-12: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/page.tsx#L10-L12
Added lines #L10 - L12 were not covered by tests

packages/ui/src/components/ui/custom/education/modules/course-module-form.tsx (1)

34-38: Confirm named export and update consumers.

CourseModuleForm was converted from a default to a named export—ensure all consuming modules import it correctly. You might also consider adding a Content-Type: application/json header to the fetch call and showing a success toast to improve UX.

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx (1)

3-5: Validate new YouTube component APIs and tests.

Imports for DeleteLinkButton, YoutubeEmbed, and YouTubeLinkForm were updated to the shared UI package. Verify their prop signatures match the updated components, and add tests to cover these lines (they’re currently untested).

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 3-5: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx#L3-L5
Added lines #L3 - L5 were not covered by tests

packages/ui/src/components/ui/custom/education/courses/course-header.tsx (2)

7-13: Consider enhancing the props interface with better TypeDocs.

The component interface is functional but could benefit from documentation for better developer experience.

 export function CourseHeader({
   href,
   data,
 }: {
+  /** URL for the course navigation link */
   href: string;
+  /** Course data object containing name and description */
   data: UserGroup;
 }) {

19-22: Consider extracting the badge styling to a reusable component.

The graduation cap badge has specific styling that could be reused across other education-related components.

Consider creating a reusable CourseBadge component:

const CourseBadge = ({ children }: { children: ReactNode }) => (
  <div className="flex items-center gap-2 rounded-lg border border-dynamic-blue/20 bg-dynamic-blue/10 px-2 text-lg text-dynamic-blue max-md:hidden">
    <GraduationCap className="h-6 w-6" />
    {children}
  </div>
);
packages/ui/src/components/ui/custom/tables/custom-data-table.tsx (2)

34-35: Remove redundant fallback values in props.

The pageIndex and pageSize already have default values calculated above, making the || 0 and || 10 fallbacks redundant.

- pageIndex={pageIndex || 0}
- pageSize={pageSize || 10}
+ pageIndex={pageIndex}
+ pageSize={pageSize}

38-42: Consider improving the search logic for better UX.

The current search implementation immediately resets all parameters when the query is empty, which might not be the desired behavior for users.

 onSearch={(query: string) =>
-  query
-    ? searchParams.set({ q: query, page: '1' })
-    : searchParams.reset()
+  query.trim()
+    ? searchParams.set({ q: query.trim(), page: '1' })
+    : searchParams.remove(['q', 'page'])
 }

This change:

  • Trims whitespace from search queries
  • Only removes search-related parameters instead of resetting everything
  • Preserves other URL parameters like pageSize
apps/web/messages/en.json (2)

287-291: Typo: inconsistent “GitHub” casing
The value uses “Github” but elsewhere we use “GitHub” (e.g., continue_with_github). Align casing for consistency.

-    "sign_in_with_github": "Sign in with Github",
+    "sign_in_with_github": "Sign in with GitHub",

1980-1987: Consistency: key naming and cross-app parity
In ws-courses you added delete_confirm_title, but the common pattern uses confirm_delete_title. Consider renaming to match (confirm_delete_title) or document the intentional change. Also ensure all new course-related keys (edit_description, course_description, card_view, table_view, no_courses_found, no_courses_found_description, no_description_provided, delete_confirm_title) are synchronized across both apps/web and apps/upskii locale files.

apps/upskii/messages/vi.json (1)

3530-3530: Typographical consistency: diacritics in “xóa”
The verb “xóa” is conventionally written with the accent on “o” (xóa), not on “á” (xoá). Consider updating to:

- "delete_confirm_title": "Xác nhận xoá \"{name}\""
+ "delete_confirm_title": "Xác nhận xóa \"{name}\""
packages/ui/src/hooks/useSearchParams.tsx (1)

96-108: Consider using Map or Object.fromEntries for cleaner implementation.

The getAll method works correctly but could be more concise.

 const getAll = useCallback(() => {
-   const params = new URLSearchParams(searchParams);
-   const entries = params.entries();
-   const result: Record<string, string[]> = {};
-   for (const [key, value] of entries) {
-     if (result[key]) {
-       result[key]?.push(value);
-     } else {
-       result[key] = [value];
-     }
-   }
-   return result;
+   const result: Record<string, string[]> = {};
+   searchParams.forEach((value, key) => {
+     result[key] = result[key] ? [...result[key], value] : [value];
+   });
+   return result;
 }, [searchParams]);
apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/page.tsx (1)

36-43: Consider adding radix parameter to parseInt calls.

While the current implementation works correctly with default values, it's a best practice to specify the radix explicitly for parseInt calls to avoid potential parsing issues with non-decimal numbers.

-  const currentPage = parseInt(page);
-  const currentPageSize = parseInt(pageSize);
+  const currentPage = parseInt(page, 10);
+  const currentPageSize = parseInt(pageSize, 10);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c1de4e8 and 194dbf4.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (63)
  • apps/upskii/messages/en.json (2 hunks)
  • apps/upskii/messages/vi.json (2 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/columns.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx (2 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/content/page.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/layout.tsx (2 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/page.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/resources/page.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/toggles.tsx (0 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/youtube-links/embed.tsx (0 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/page.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx (1 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/columns.tsx (0 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/new-actions.tsx (0 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/page.tsx (0 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/row-actions.tsx (0 hunks)
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/table.tsx (0 hunks)
  • apps/upskii/src/components/custom-data-table.tsx (1 hunks)
  • apps/upskii/src/utils/path-helper.ts (0 hunks)
  • apps/web/messages/en.json (2 hunks)
  • apps/web/messages/vi.json (2 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/drive/form.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/drive/new-actions.tsx (1 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/columns.tsx (1 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/form.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/layout.tsx (2 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/link-button.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx (2 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/layout.tsx (2 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx (4 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/delete-resource.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/file-display.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/page.tsx (1 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/pdf-viewer.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/delete-link.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/form.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx (1 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/page.tsx (1 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/row-actions.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/section.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/columns.tsx (1 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/form.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/page.tsx (6 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/row-actions.tsx (0 hunks)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/quiz-sets/[setId]/layout.tsx (1 hunks)
  • apps/web/src/components/custom-data-table.tsx (1 hunks)
  • packages/ui/package.json (2 hunks)
  • packages/ui/src/components/ui/custom/education/courses/course-card-view.tsx (2 hunks)
  • packages/ui/src/components/ui/custom/education/courses/course-form.tsx (1 hunks)
  • packages/ui/src/components/ui/custom/education/courses/course-header.tsx (1 hunks)
  • packages/ui/src/components/ui/custom/education/courses/course-row-actions.tsx (2 hunks)
  • packages/ui/src/components/ui/custom/education/modules/course-module-form.tsx (1 hunks)
  • packages/ui/src/components/ui/custom/education/modules/course-module-row-actions.tsx (2 hunks)
  • packages/ui/src/components/ui/custom/education/modules/module-toggle.tsx (1 hunks)
  • packages/ui/src/components/ui/custom/education/modules/resources/delete-resource.tsx (1 hunks)
  • packages/ui/src/components/ui/custom/education/modules/resources/file-display.tsx (1 hunks)
  • packages/ui/src/components/ui/custom/education/modules/resources/file-upload-form.tsx (2 hunks)
  • packages/ui/src/components/ui/custom/tables/custom-data-table.tsx (1 hunks)
  • packages/ui/src/components/ui/text-editor/content-editor.tsx (1 hunks)
  • packages/ui/src/hooks/useSearchParams.tsx (1 hunks)
  • packages/utils/src/path-helper.ts (1 hunks)
💤 Files with no reviewable changes (20)
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/link-button.tsx
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/new-actions.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/delete-resource.tsx
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/youtube-links/embed.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/form.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/row-actions.tsx
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/table.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/row-actions.tsx
  • apps/upskii/src/utils/path-helper.ts
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/form.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/form.tsx
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/columns.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/section.tsx
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/page.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/pdf-viewer.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/delete-link.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/file-display.tsx
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/toggles.tsx
  • apps/web/src/app/[locale]/(dashboard)/[wsId]/drive/form.tsx
  • apps/upskii/src/app/[locale]/(dashboard)/[wsId]/drive/row-actions.tsx
🧰 Additional context used
🧬 Code Graph Analysis (5)
packages/ui/src/components/ui/custom/education/courses/course-row-actions.tsx (1)
packages/ui/src/components/ui/custom/education/courses/course-form.tsx (1)
  • CourseForm (34-125)
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx (1)
packages/ui/src/components/ui/custom/education/courses/course-header.tsx (1)
  • CourseHeader (7-38)
packages/ui/src/components/ui/custom/education/courses/course-header.tsx (1)
packages/types/src/primitives/UserGroup.ts (1)
  • UserGroup (3-10)
apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/layout.tsx (1)
packages/ui/src/components/ui/custom/education/courses/course-header.tsx (1)
  • CourseHeader (7-38)
apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/page.tsx (5)
packages/ui/src/components/ui/custom/education/courses/course-form.tsx (1)
  • CourseForm (34-125)
packages/ui/src/components/ui/custom/view-toggle.tsx (1)
  • ViewToggle (19-62)
packages/ui/src/components/ui/custom/education/courses/course-card-view.tsx (1)
  • CourseCardView (18-123)
packages/ui/src/components/ui/custom/education/courses/course-pagination.tsx (1)
  • CoursePagination (15-56)
packages/types/src/db.ts (1)
  • WorkspaceCourse (37-39)
🪛 GitHub Check: codecov/patch
apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx

[warning] 3-5: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx#L3-L5
Added lines #L3 - L5 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx

[warning] 5-8: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx#L5-L8
Added lines #L5 - L8 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/content/page.tsx

[warning] 5-7: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/content/page.tsx#L5-L7
Added lines #L5 - L7 were not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/page.tsx

[warning] 5-5: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/page.tsx#L5
Added line #L5 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/page.tsx

[warning] 7-7: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/page.tsx#L7
Added line #L7 was not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/page.tsx

[warning] 2-4: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/page.tsx#L2-L4
Added lines #L2 - L4 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx

[warning] 10-10: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx#L10
Added line #L10 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/page.tsx

[warning] 10-12: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/page.tsx#L10-L12
Added lines #L10 - L12 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/columns.tsx

[warning] 5-5: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/columns.tsx#L5
Added line #L5 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/resources/page.tsx

[warning] 2-4: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/resources/page.tsx#L2-L4
Added lines #L2 - L4 were not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx

[warning] 3-5: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx#L3-L5
Added lines #L3 - L5 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx

[warning] 3-3: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx#L3
Added line #L3 was not covered by tests


[warning] 23-23: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx#L23
Added line #L23 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/layout.tsx

[warning] 44-44: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/layout.tsx#L44
Added line #L44 was not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/columns.tsx

[warning] 5-5: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/columns.tsx#L5
Added line #L5 was not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/layout.tsx

[warning] 6-7: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/layout.tsx#L6-L7
Added lines #L6 - L7 were not covered by tests


[warning] 42-42: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/layout.tsx#L42
Added line #L42 was not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/layout.tsx

[warning] 3-3: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/layout.tsx#L3
Added line #L3 was not covered by tests


[warning] 23-25: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/layout.tsx#L23-L25
Added lines #L23 - L25 were not covered by tests

apps/upskii/src/components/custom-data-table.tsx

[warning] 3-7: apps/upskii/src/components/custom-data-table.tsx#L3-L7
Added lines #L3 - L7 were not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx

[warning] 5-6: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L5-L6
Added lines #L5 - L6 were not covered by tests


[warning] 9-14: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L9-L14
Added lines #L9 - L14 were not covered by tests


[warning] 16-17: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L16-L17
Added lines #L16 - L17 were not covered by tests


[warning] 20-36: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L20-L36
Added lines #L20 - L36 were not covered by tests


[warning] 56-60: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L56-L60
Added lines #L56 - L60 were not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx

[warning] 9-12: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx#L9-L12
Added lines #L9 - L12 were not covered by tests


[warning] 22-23: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx#L22-L23
Added lines #L22 - L23 were not covered by tests


[warning] 73-73: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx#L73
Added line #L73 was not covered by tests


[warning] 80-84: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx#L80-L84
Added lines #L80 - L84 were not covered by tests


[warning] 195-195: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx#L195
Added line #L195 was not covered by tests

🔇 Additional comments (59)
apps/web/src/app/[locale]/(dashboard)/[wsId]/education/quiz-sets/[setId]/layout.tsx (1)

3-3: Verify LinkButton import and usage.

Importing LinkButton from the shared UI package centralizes component usage. Confirm that the public API for LinkButton (props like href, title, icon, and className) hasn't changed with this migration so that this file compiles and behaves as expected.

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx (4)

6-6: Approved: Centralized CourseCardView import
The local card view component has been replaced with the shared package variant, improving consistency and maintainability across apps.


7-7: Approved: Centralized CourseForm import
Pulling in the form component from the UI package reduces duplication and ensures a uniform user experience.


8-8: Approved: Centralized CoursePagination import
Standardizing pagination through the shared package will streamline styling and behavior across different listings.


10-10: Add test coverage for the new ViewToggle import
Static analysis flagged this line as uncovered. Please add unit or integration tests to validate that toggling between “card” and “table” views updates the URL search parameter and renders the correct component.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 10-10: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/page.tsx#L10
Added line #L10 was not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/drive/new-actions.tsx (1)

4-7: LGTM! Clean migration to shared UI package.

The import change correctly moves from local components to the centralized @tuturuuu/ui package, aligning perfectly with the PR objectives.

packages/ui/src/components/ui/custom/education/modules/resources/file-upload-form.tsx (2)

20-20: LGTM! Consistent migration to shared utilities.

The import change correctly uses the centralized path utilities from @tuturuuu/utils, maintaining consistency with the broader refactoring effort.


320-320: Good UX improvement with proper text handling.

The addition of max-w-[400px] and break-all classes improves the display of long file names by constraining width and ensuring text wraps properly.

packages/utils/src/path-helper.ts (2)

1-51: Excellent robust path joining implementation.

The joinPath function demonstrates comprehensive edge case handling:

  • Proper empty input handling
  • Preservation of relative/absolute path semantics
  • Correct trailing slash behavior
  • Filtering of invalid segments

The logic correctly handles complex scenarios like mixed relative/absolute paths while maintaining path integrity.


53-93: Well-implemented parent path extraction.

The popPath function provides robust parent directory extraction with:

  • Proper path normalization
  • Correct handling of relative vs absolute paths
  • Edge case management for root and empty paths
  • Preservation of original path format

The implementation thoughtfully handles various path scenarios while maintaining semantic correctness.

packages/ui/package.json (1)

60-60:

❓ Verification inconclusive

Verify Framer Motion’s peerDependencies against React version
framer-motion@^12.16.0 may have a React peerDependency (e.g., React 18). Please confirm it’s compatible with your React ^19.1.0 to avoid runtime mismatches.


🌐 Web query:

What are the peerDependencies of framer-motion@12.16.0?

💡 Result:

The framer-motion package version 12.16.0 specifies the following peer dependencies:

  • @emotion/is-prop-valid
  • react
  • react-dom

These dependencies are essential for the proper functioning of framer-motion. The @emotion/is-prop-valid package is optional, as indicated by its absence in the peerDependenciesMeta section. (app.unpkg.com)

It's important to note that framer-motion is compatible with React versions 18 and 19. However, there have been reported issues with React 19, such as incompatibilities affecting type definitions and element animations. (github.com) Therefore, if you're using React 19, ensure that you have the latest versions of both react and react-dom to mitigate potential issues.

Citations:


Verify Framer Motion’s peerDependencies and React 19 compatibility

  • File: packages/ui/package.json (line 60)
  • framer-motion@^12.16.0 declares peerDependencies:
    • react
    • react-dom
    • @emotion/is-prop-valid
  • Although v12.16.0 supports React 18 and 19, there are known issues with React 19 (type definitions and element animations). Ensure your react and react-dom packages are updated to ≥19.1.0 and test key animations to avoid runtime mismatches.
apps/upskii/src/components/custom-data-table.tsx (1)

3-3: Re-export simplifies component usage
The CustomDataTable is now delegated to the shared UI library. Confirm that the export path (@tuturuuu/ui/custom/tables/custom-data-table) is valid in the packages/ui package.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 3-7: apps/upskii/src/components/custom-data-table.tsx#L3-L7
Added lines #L3 - L7 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/content/page.tsx (1)

5-5: Centralize the editor import
Replacing the local ModuleContentEditor with the shared component from @tuturuuu/ui/text-editor/content-editor is correct. Ensure the external component’s API (courseId, moduleId, content) aligns with your usage here.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 5-7: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/content/page.tsx#L5-L7
Added lines #L5 - L7 were not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/page.tsx (1)

5-5: Migrate to shared CourseModuleForm
Importing CourseModuleForm from @tuturuuu/ui/custom/education/modules/course-module-form replaces the local implementation appropriately. Verify that the new form component supports the wsId and courseId props as passed in <CourseModuleForm wsId={wsId} courseId={courseId} />.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 5-5: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/page.tsx#L5
Added line #L5 was not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/columns.tsx (1)

5-5: Use centralized row actions
Updating the import to @tuturuuu/ui/custom/education/courses/course-row-actions for WorkspaceCourseRowActions aligns with the new package structure. Ensure the external component’s API matches its usage in the actions column.

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx (1)

5-5: Align row actions import
Switching to the shared WorkspaceCourseRowActions from @tuturuuu/ui/custom/education/courses/course-row-actions is correct. Confirm that the package export and the component props remain compatible with this table’s actions cell.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 5-8: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/columns.tsx#L5-L8
Added lines #L5 - L8 were not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/columns.tsx (1)

5-5: Import shared row actions
Switched to using the centralized WorkspaceCourseModuleRowActions from the @tuturuuu/ui package, improving consistency and reducing duplication.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 5-5: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/columns.tsx#L5
Added line #L5 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/page.tsx (1)

7-7: Use shared CourseModuleForm component
Replaced local form import with the package version to align with the shared UI library migration.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 7-7: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/page.tsx#L7
Added line #L7 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/columns.tsx (1)

5-5: Centralize module row actions import
Updated import to pull WorkspaceCourseModuleRowActions from the shared UI package for consistency.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 5-5: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/columns.tsx#L5
Added line #L5 was not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx (1)

3-5: Migrate YouTube components to shared UI package
All YouTube-related components (DeleteLinkButton, YoutubeEmbed, YouTubeLinkForm) now come from the @tuturuuu/ui library, reducing local duplication.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 3-5: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/youtube-links/page.tsx#L3-L5
Added lines #L3 - L5 were not covered by tests

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/resources/page.tsx (1)

2-4: Switch resource components to shared package
Replaced local resource UI components (DeleteResourceButton, FileDisplay, StorageObjectForm) with imports from @tuturuuu/ui to consolidate implementations.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 2-4: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/resources/page.tsx#L2-L4
Added lines #L2 - L4 were not covered by tests

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/page.tsx (1)

2-4:

✅ Verification successful

Import migration looks good - verify component availability.

The import path updates successfully migrate these components to the centralized UI package, aligning with the PR objectives. The component usage remains unchanged, which ensures functional consistency.

Run this script to verify the migrated components exist at their new package locations:


🏁 Script executed:

#!/bin/bash
# Description: Verify that the migrated UI components exist in the packages/ui directory

echo "Checking for DeleteResourceButton component..."
fd "delete-resource" packages/ui --type f --exec cat {} \; | head -10

echo -e "\nChecking for FileDisplay component..."
fd "file-display" packages/ui --type f --exec cat {} \; | head -10

echo -e "\nChecking for StorageObjectForm component..."
fd "file-upload-form" packages/ui --type f --exec cat {} \; | head -10

Length of output: 1483


Import migration successful – all components verified

I confirmed that the following components are exported from the new @tuturuuu/ui package paths:

  • DeleteResourceButton in packages/ui/custom/education/modules/resources/delete-resource
  • FileDisplay in packages/ui/custom/education/modules/resources/file-display
  • StorageObjectForm in packages/ui/custom/education/modules/resources/file-upload-form

No further changes needed.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 2-4: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/resources/page.tsx#L2-L4
Added lines #L2 - L4 were not covered by tests

packages/ui/src/components/ui/custom/education/courses/course-card-view.tsx (1)

78-79: Consistent scoped translation key usage.

The fallback text properly uses the new scoped translation key, maintaining consistency with the no_courses_found key update.

packages/ui/src/components/ui/text-editor/content-editor.tsx (1)

16-16:

✅ Verification successful

Export standardization improves consistency.

The change from default to named export aligns with the standardization pattern across the migrated UI components.

Run this script to verify all imports of ModuleContentEditor use the correct named import syntax:


🏁 Script executed:

#!/bin/bash
# Description: Verify that all imports of ModuleContentEditor use named import syntax

echo "Searching for ModuleContentEditor imports..."
rg "import.*ModuleContentEditor" -A 1 -B 1

echo -e "\nChecking for any remaining default imports that need updating..."
rg "import ModuleContentEditor" --type ts --type tsx

Length of output: 1379


Let’s adjust the import check to use glob patterns so we include both .ts and .tsx files:


🏁 Script executed:

#!/bin/bash
echo "Re-verifying default imports for ModuleContentEditor across TS and TSX files..."
rg "import\s\+ModuleContentEditor\b" -g '*.ts' -g '*.tsx' || echo "No default imports found; all imports are correctly named."

Length of output: 355


All ModuleContentEditor imports now use named exports

Verification across .ts and .tsx files confirms no remaining default imports. The change in
packages/ui/src/components/ui/text-editor/content-editor.tsx is fully propagated:

  • apps/web/src/app/[locale]/…/content/page.tsx
  • apps/upskii/src/app/[locale]/…/content/page.tsx

No further action required—approving this change.

packages/ui/src/components/ui/custom/education/courses/course-row-actions.tsx (1)

6-6: Shared CourseForm import applied
Import updated to the centralized UI package path. Confirm CourseForm is a named export in @tuturuuu/ui/custom/education/courses/course-form.

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/modules/[moduleId]/layout.tsx (1)

6-7: Shared UI import migration validated
The LinkButton default import and ModuleToggles named import now reference the shared UI package. Ensure the package exports match these import styles.

packages/ui/src/components/ui/custom/education/modules/course-module-row-actions.tsx (1)

6-6: CourseModuleForm import migration approved
Import updated to the shared UI package. Confirm @tuturuuu/ui/custom/education/modules/course-module-form exports CourseModuleForm as a named export.

apps/upskii/messages/en.json (1)

3527-3530: Scoped localization keys are correctly added under ws-courses.

Adding table_view, no_courses_found, and no_description_provided under the ws-courses namespace aligns with the shared UI package and prevents key collisions.

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/layout.tsx (2)

6-7: LGTM! Import paths successfully migrated to centralized UI package.

The imports have been correctly updated to use the centralized UI components from @tuturuuu/ui/custom/education/modules, which improves code modularity and reusability across the codebase.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 6-7: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/layout.tsx#L6-L7
Added lines #L6 - L7 were not covered by tests


42-42: Good refactoring! Path construction improved using commonHref variable.

Using the commonHref variable for path construction reduces duplication and makes the code more maintainable compared to the previous manual string concatenation approach.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 42-42: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/layout.tsx#L42
Added line #L42 was not covered by tests

packages/ui/src/components/ui/custom/education/modules/resources/file-display.tsx (2)

11-14: LGTM! Dynamic import path correctly updated for centralized PDF viewer.

The dynamic import has been properly updated to reference the PDFViewer component from its new location in the UI package, maintaining the same SSR disabling behavior.


20-20: Export style changed from default to named export.

The component export has been changed from default to named export. This is a breaking change, but appears to be part of the coordinated UI migration effort where all consumers have been updated accordingly.

apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx (2)

3-3: LGTM! CourseHeader import correctly added from UI package.

The import path for the CourseHeader component follows the expected pattern for the centralized UI components.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 3-3: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx#L3
Added line #L3 was not covered by tests


23-23: LGTM! CourseHeader usage is correct.

The CourseHeader component is properly instantiated with the required props:

  • data (UserGroup type from getData function)
  • href (correctly constructed course URL path)

This matches the expected interface shown in the relevant code snippet.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 23-23: apps/upskii/src/app/[locale]/(dashboard)/[wsId]/courses/[courseId]/layout.tsx#L23
Added line #L23 was not covered by tests

apps/web/messages/vi.json (2)

285-289: New common UI translation keys added correctly.

The entries for "sidebar", "whats_the_title", "write_something", "error_saving_content", and "not_completed" align with the corresponding English keys and follow the existing JSON structure without syntax issues.


1992-1998: ws-courses localization keys are correctly scoped.

The new keys under "ws-courses"—including "edit_description", "course_description", "card_view", "table_view", "no_courses_found", "no_description_provided", and "delete_confirm_title"—match their English counterparts and properly escape the {name} placeholder.

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/layout.tsx (1)

3-3: LGTM! Clean migration to shared component.

The import of the shared CourseHeader component aligns well with the PR objective of migrating common UI components to the packages/ui directory.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 3-3: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/layout.tsx#L3
Added line #L3 was not covered by tests

packages/ui/src/components/ui/custom/education/courses/course-header.tsx (1)

27-27:

✅ Verification successful

Verify the translation key 'common.unknown' exists.

Ensure the fallback translation key is available across all locales to prevent missing translation errors.


🏁 Script executed:

#!/bin/bash
# Description: Verify the translation key exists in all locale files

# Search for the common.unknown translation key
rg -A 2 -B 2 '"unknown"' apps/*/messages/*.json packages/*/messages/*.json || echo "Translation key not found"

# Check the structure of common translations
rg -A 5 '"common"' apps/*/messages/*.json packages/*/messages/*.json | head -20

Length of output: 5404


common.unknown translation verified across all locales
All applications (calendar, famigo, nova, rewise, upskii, web) define common.unknown in both en.json and vi.json under their common section. No missing‐translation risk detected—no further action required.

apps/web/messages/en.json (1)

288-291: Review new common keys for translation consistency
The keys whats_the_title, write_something, error_saving_content, and not_completed are newly added under common. Please verify they are also present (and translated) in all other locale files (e.g., vi.json, apps/upskii/messages/en.json).

apps/upskii/messages/vi.json (3)

3528-3529: Approve translations for empty-state messages
The translations for no_courses_found (“Chưa có khóa học nào”) and no_description_provided (“Không có mô tả”) are accurate and contextually appropriate.


3527-3527:

✅ Verification successful

Verify table_view key alignment
Ensure the table_view key matches the corresponding key in en.json (it might be table-view or table_view) to avoid locale mismatches.


🏁 Script executed:

rg '"table[-_]view"' -n apps/upskii/messages/en.json

Length of output: 91


No action needed: table_view key is aligned
The table_view key in apps/upskii/messages/vi.json matches the corresponding entry in apps/upskii/messages/en.json (both at line 3527). No changes required.


1-4138:

✅ Verification successful

Verify removal of deprecated top-level keys
Confirm that the old top-level entries "no-courses-found" and "no-description-provided" have been removed to prevent fallback translation conflicts.


🏁 Script executed:

rg -e '"no-courses-found"' -e '"no-description-provided"' -n apps/upskii/messages/vi.json

Length of output: 91


Removal of deprecated keys confirmed
No occurrences of "no-courses-found" or "no-description-provided" remain in apps/upskii/messages/vi.json.

apps/web/src/components/custom-data-table.tsx (1)

3-3: LGTM! Clean migration to shared UI package.

The re-export pattern maintains backward compatibility while centralizing the CustomDataTable implementation in the shared UI library. This improves maintainability and code reuse across the application.

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx (3)

9-12: LGTM! Proper migration to shared UI components.

The import updates to use components from @tuturuuu/ui/custom/education/modules align well with the centralization effort, improving code reusability and maintainability.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 9-12: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx#L9-L12
Added lines #L9 - L12 were not covered by tests


73-73: Good UX improvement with Accordion wrapper.

Using Accordion with type="multiple" allows users to expand multiple course sections simultaneously, providing a better user experience for exploring module content.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 73-73: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx#L73
Added line #L73 was not covered by tests


80-84: Proper integration of RichTextEditor for content display.

The RichTextEditor with readOnly prop and proper typing as JSONContent is well-implemented. The scrollable container with max height prevents excessive vertical space usage.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 80-84: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/page.tsx#L80-L84
Added lines #L80 - L84 were not covered by tests

packages/ui/src/hooks/useSearchParams.tsx (2)

39-62: Excellent parameter manipulation methods with proper performance optimization.

The set, add, and remove methods are well-implemented with:

  • Proper use of useCallback for performance
  • Correct dependency arrays
  • Appropriate handling of array values
  • Optional refresh parameter for flexibility

112-124: Well-structured return object with comprehensive API.

The hook provides a clean, comprehensive API with:

  • Consistent naming conventions
  • Alias for clearreset
  • Good balance of utility methods
  • Proper TypeScript typing
apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/page.tsx (6)

2-2: Mock data import commented out appropriately.

Good practice to keep the mock import commented for potential development use while maintaining clean production code.


6-10: Well-structured migration to shared UI components.

The import changes properly migrate from local components to the shared @tuturuuu/ui package, which aligns perfectly with the PR objective. The new components (CourseCardView, CourseForm, CoursePagination, ViewToggle) provide enhanced functionality for the course management interface.


20-20: Type-safe view parameter added.

The union type 'card' | 'table' provides good type safety for the view toggle functionality.


53-93: Excellent implementation of view toggle functionality.

The conditional rendering logic is clean and well-structured. The layout properly accommodates both card and table views with appropriate spacing and components. The pagination integration is correctly implemented for the card view.


107-125: Mock data logic properly preserved for development.

The commented mock data implementation is well-structured and provides a good fallback for development environments. This allows for testing pagination and filtering logic without requiring database access.


159-159: Excellent type safety improvement.

The explicit return type annotation enhances type safety and makes the function's contract clear, especially with the added modules property. This helps prevent type-related issues and improves IDE support.

apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx (5)

1-1: LGTM! Import migration looks good.

The migration from local imports to shared @tuturuuu/ui package imports aligns well with the PR objectives for centralizing UI components.

Also applies to: 5-6


9-14: LGTM! Proper typing for Next.js app router.

The Props interface correctly types the async params pattern used in Next.js 13+ app router.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 9-14: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L9-L14
Added lines #L9 - L14 were not covered by tests


16-17: LGTM! Correct async pattern for Next.js app router.

The function signature and params destructuring follow the proper Next.js app router async component pattern.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 16-17: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L16-L17
Added lines #L16 - L17 were not covered by tests


56-60: LGTM! Clean component integration.

The ModuleContentEditor integration looks clean with proper prop passing of courseId, moduleId, and fetched content.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 56-60: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L56-L60
Added lines #L56 - L60 were not covered by tests


20-26:

❓ Verification inconclusive

Consider adding authorization checks.

The database query fetches content based on moduleId and courseId but doesn't verify user permissions. Ensure that upstream middleware or route guards validate user access to this specific course and module.


🏁 Script executed:

#!/bin/bash
# Description: Check for authorization/permission validation in the route
# Expected: Find middleware, guards, or permission checks for course/module access

rg -A 5 -B 5 "auth|permission|guard|middleware" apps/web/src/app/\[locale\]/\(dashboard\)/\[wsId\]/education/courses/\[courseId\]/modules/\[moduleId\]/

Length of output: 145


I’ll search for any Next.js middleware or route handler files in the app directory to see if access is being enforced elsewhere.


🏁 Script executed:

#!/bin/bash
# Search for Next.js middleware and route handlers in the app directory
find apps/web/src/app -type f \( -iname 'middleware.ts' -o -iname 'route.ts' -o -iname 'route.tsx' \)

Length of output: 14386


I’ll inspect the dashboard layout for any auth guards and also pull in the createClient() implementation to see if it already ties into user sessions.


🏁 Script executed:

#!/bin/bash
# 1) Show the (dashboard) layout.tsx to check for any AuthGuard or session checks
find apps/web/src/app -type f -path 'apps/web/src/app/*/(dashboard)/layout.tsx' -print -exec head -n 50 {} \;

# 2) Locate and print the wrapper for createClient() to see how it handles auth
grep -R "export function createClient" -n apps/web/src
grep -R "createClient(" -n apps/web/src/lib apps/web/src

Length of output: 65485


Verify and enforce workspace/course access before loading module content

I didn’t find any permission checks in either the content-page or your global middleware. While authentication is enforced, there’s no validation that the signed-in user belongs to this workspace or is enrolled in the specified course/module.

• apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx
• apps/web/src/middleware.ts

Please confirm that Supabase RLS policies restrict workspace_course_modules to authorized users, or add one of the following:

  • a filter on workspace_id (or a membership join) in your query
  • a guard in the relevant layout.tsx or custom middleware
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 20-36: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L20-L36
Added lines #L20 - L36 were not covered by tests

}
description={
<div className="mt-2 line-clamp-2">
{data.description || t('ws-courses.no_description_provided')}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the translation key 'ws-courses.no_description_provided' exists.

Ensure this specific translation key is available to prevent runtime errors when course descriptions are missing.


🏁 Script executed:

#!/bin/bash
# Description: Verify the ws-courses translation keys exist

# Search for ws-courses translation namespace
rg -A 10 '"ws-courses"' apps/*/messages/*.json packages/*/messages/*.json

# Look for the specific no_description_provided key
rg '"no_description_provided"' apps/*/messages/*.json packages/*/messages/*.json

Length of output: 8811


Add missing translation key ws-courses.no_description_provided across all apps

The key is defined in:

  • apps/web/messages/{en.json, vi.json}
  • apps/upskii/messages/{en.json, vi.json}

But it’s missing in:

  • apps/calendar/messages/{en.json, vi.json}
  • apps/famigo/messages/{en.json, vi.json}
  • apps/nova/messages/{en.json, vi.json}
  • apps/rewise/messages/{en.json, vi.json}

Please add the following under the ws-courses namespace in each of those files:

  "no_description_provided": "No description provided"

(and the corresponding localized text in vi.json)

🤖 Prompt for AI Agents
In packages/ui/src/components/ui/custom/education/courses/course-header.tsx at
line 33, the translation key 'ws-courses.no_description_provided' is used but
missing in some app localization files. To fix this, add the key
"no_description_provided": "No description provided" under the "ws-courses"
namespace in the en.json and vi.json files of apps/calendar, apps/famigo,
apps/nova, and apps/rewise to ensure consistent translations and prevent runtime
errors.

Comment on lines +23 to +25
const pageSize = Number(searchParams.get('pageSize') || 10);
const page = Number(searchParams.get('page') || 0);
const pageIndex = page > 0 ? page - 1 : 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix potential issues with URL parameter parsing and page indexing.

The current implementation has several concerns:

  1. No validation for invalid number inputs
  2. Page indexing logic may be confusing (page 0 vs page 1)
  3. Default values could be inconsistent
- const pageSize = Number(searchParams.get('pageSize') || 10);
- const page = Number(searchParams.get('page') || 0);
- const pageIndex = page > 0 ? page - 1 : 0;
+ const pageSize = Math.max(1, Number(searchParams.get('pageSize')) || 10);
+ const page = Math.max(1, Number(searchParams.get('page')) || 1);
+ const pageIndex = page - 1; // Convert 1-based page to 0-based index

This ensures:

  • Minimum page size of 1
  • 1-based page numbering for URLs (more user-friendly)
  • Consistent conversion to 0-based indexing
📝 Committable suggestion

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

Suggested change
const pageSize = Number(searchParams.get('pageSize') || 10);
const page = Number(searchParams.get('page') || 0);
const pageIndex = page > 0 ? page - 1 : 0;
const pageSize = Math.max(1, Number(searchParams.get('pageSize')) || 10);
const page = Math.max(1, Number(searchParams.get('page')) || 1);
const pageIndex = page - 1; // Convert 1-based page to 0-based index
🤖 Prompt for AI Agents
In packages/ui/src/components/ui/custom/tables/custom-data-table.tsx around
lines 23 to 25, improve the parsing of URL parameters by validating that
pageSize and page are valid positive numbers, defaulting pageSize to at least 1.
Adjust the page variable to use 1-based indexing from the URL, then convert it
consistently to 0-based pageIndex for internal use. This ensures robust handling
of invalid inputs and clearer, user-friendly page numbering.

Comment on lines +28 to +51
<Suspense fallback={null}>
<DataTable
t={t}
hideToolbar={hideToolbar}
hidePagination={hidePagination}
namespace={namespace}
pageIndex={pageIndex || 0}
pageSize={pageSize || 10}
onRefresh={() => router.refresh()}
defaultQuery={searchParams.getSingle('q', '')}
onSearch={(query: string) =>
query
? searchParams.set({ q: query, page: '1' })
: searchParams.reset()
}
setParams={(params) => searchParams.set(params)}
resetParams={() => searchParams.reset()}
isEmpty={searchParams.isEmpty}
newObjectTitle={t('common.create')}
className={className}
{...props}
/>
</Suspense>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the useSearchParams hook implementation.

The component heavily relies on a custom useSearchParams hook that provides methods like set, reset, getSingle, etc. Ensure this hook is properly implemented and tested.


🏁 Script executed:

#!/bin/bash
# Description: Verify the useSearchParams hook implementation and usage

# Find the useSearchParams hook implementation
fd "useSearchParams.tsx" packages/ui/src/hooks/ --exec cat {} \;

# Check for other usages of this hook to understand patterns
rg -A 5 -B 2 "useSearchParams" packages/ui/src/ --type tsx --type ts

Length of output: 3321


🏁 Script executed:

#!/bin/bash
# Check for existing tests referencing useSearchParams
rg "useSearchParams" --glob "*.{test,spec}.{ts,tsx}" -n packages/ui

Length of output: 67


Add unit tests for useSearchParams hook

We rely heavily on the custom hook at packages/ui/src/hooks/useSearchParams.tsx (methods: has, get, getSingle, set, add, remove, reset/clear, getAll, isEmpty), but no tests currently cover its behavior. Please add unit tests to verify:

  • Fallback values returned by getSingle and get
  • URL updates and refresh calls in set, add, and remove
  • Reset behavior via reset/clear
  • Correct mapping from getAll
  • isEmpty flag toggles as expected

Path to cover:

  • packages/ui/src/hooks/useSearchParams.tsx
🤖 Prompt for AI Agents
In packages/ui/src/hooks/useSearchParams.tsx, add comprehensive unit tests to
cover the custom hook's behavior. Write tests to verify fallback values returned
by getSingle and get methods, ensure URL updates and refresh calls occur
correctly when using set, add, and remove methods, validate reset and clear
methods properly reset the state, confirm getAll returns the correct mapping of
parameters, and check that the isEmpty flag toggles as expected. Use a testing
framework like Jest and mock necessary dependencies such as router or URL
handling to simulate and assert these behaviors.

Comment on lines +20 to +30
const get = useCallback(
(data: { key: string; fallbackValue?: string | string[] } | string) => {
if (typeof data === 'string') return searchParams.getAll(data) || '';

const { key, fallbackValue } = data;
return searchParams.getAll(key).length > 0
? searchParams.getAll(key) || fallbackValue
: fallbackValue;
},
[searchParams]
);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix inconsistent return types and logic in the get method.

The get method has several issues:

  1. Inconsistent return types: Returns string[] or string | string[] | undefined depending on the input
  2. Logic flaw: Line 22 returns string[] but the fallback '' is a string
  3. Redundant condition: Line 25-26 checks length > 0 then calls getAll again
 const get = useCallback(
-   (data: { key: string; fallbackValue?: string | string[] } | string) => {
-     if (typeof data === 'string') return searchParams.getAll(data) || '';
-
-     const { key, fallbackValue } = data;
-     return searchParams.getAll(key).length > 0
-       ? searchParams.getAll(key) || fallbackValue
-       : fallbackValue;
+   (data: { key: string; fallbackValue?: string[] } | string): string[] => {
+     if (typeof data === 'string') {
+       return searchParams.getAll(data);
+     }
+
+     const { key, fallbackValue = [] } = data;
+     const values = searchParams.getAll(key);
+     return values.length > 0 ? values : fallbackValue;
   },
   [searchParams]
 );
📝 Committable suggestion

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

Suggested change
const get = useCallback(
(data: { key: string; fallbackValue?: string | string[] } | string) => {
if (typeof data === 'string') return searchParams.getAll(data) || '';
const { key, fallbackValue } = data;
return searchParams.getAll(key).length > 0
? searchParams.getAll(key) || fallbackValue
: fallbackValue;
},
[searchParams]
);
const get = useCallback(
(data: { key: string; fallbackValue?: string[] } | string): string[] => {
if (typeof data === 'string') {
return searchParams.getAll(data);
}
const { key, fallbackValue = [] } = data;
const values = searchParams.getAll(key);
return values.length > 0 ? values : fallbackValue;
},
[searchParams]
);
🤖 Prompt for AI Agents
In packages/ui/src/hooks/useSearchParams.tsx around lines 20 to 30, the get
method returns inconsistent types by mixing string arrays and strings, returns
an empty string fallback where a string array is expected, and redundantly calls
searchParams.getAll twice. Fix this by ensuring the method consistently returns
string arrays or the fallback, replace the empty string fallback with an empty
array, and store the result of searchParams.getAll in a variable to avoid
duplicate calls and simplify the conditional logic.

Comment on lines +20 to +36
const getContent = async (courseId: string, moduleId: string) => {
const supabase = await createClient();
const { data, error } = await supabase
.from('workspace_course_modules')
.select('content')
.eq('id', moduleId)
.eq('course_id', courseId);

if (error) {
console.error(error);
}

return data?.[0]?.content as JSONContent;
};

const content = await getContent(courseId, moduleId);

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling and consider function placement.

The content fetching logic has several areas for improvement:

  1. Error handling: Currently only logs errors to console, which isn't sufficient for production
  2. Function placement: Defining the function inside the component isn't optimal
  3. Type safety: The as JSONContent assertion could be unsafe without validation

Consider this refactor:

-  const getContent = async (courseId: string, moduleId: string) => {
-    const supabase = await createClient();
-    const { data, error } = await supabase
-      .from('workspace_course_modules')
-      .select('content')
-      .eq('id', moduleId)
-      .eq('course_id', courseId);
-
-    if (error) {
-      console.error(error);
-    }
-
-    return data?.[0]?.content as JSONContent;
-  };
+  const supabase = await createClient();
+  const { data, error } = await supabase
+    .from('workspace_course_modules')
+    .select('content')
+    .eq('id', moduleId)
+    .eq('course_id', courseId)
+    .single();
+
+  if (error) {
+    console.error('Failed to fetch module content:', error);
+    // Consider redirecting to error page or showing error UI
+    throw new Error('Failed to load module content');
+  }
+
+  const content = data?.content as JSONContent;

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 20-36: apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx#L20-L36
Added lines #L20 - L36 were not covered by tests

🤖 Prompt for AI Agents
In
apps/web/src/app/[locale]/(dashboard)/[wsId]/education/courses/[courseId]/modules/[moduleId]/content/page.tsx
around lines 20 to 36, improve error handling by throwing or properly handling
the error instead of just logging it, move the getContent function outside the
component to avoid redefining it on each render, and add validation or type
checking before asserting the returned data as JSONContent to ensure type
safety.

Copy link
Member

@vhpx vhpx left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks @VNOsST.

@vhpx vhpx enabled auto-merge June 9, 2025 06:03
@vhpx vhpx disabled auto-merge June 9, 2025 09:23
@vhpx vhpx merged commit cf76472 into main Jun 9, 2025
19 checks passed
@vhpx vhpx deleted the chore/education-packages branch June 9, 2025 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants