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

[WC-28, WC-162] - Implement Client/Chef profile page, Implement edit Client/Chef profile page + API connection #21

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

Conversation

tong788
Copy link
Collaborator

@tong788 tong788 commented Nov 23, 2024

Description

This page is about the profile and edit profile page of client and chef user.

What

It is the page where a user can see their account profile and they can also edit their profile. There are lots of field such as firstname, lastname, gender, phone, email, etc.
Client Profile
image
image
Client Edit Profile
image
Chef Profile (unfinished, chef rating left)
image
Chef Edit Profile
image

Why

Because a user profile need to be handleable, so they can adjust their profile to relate with present information.

Type of change

Please check the relevent option with x.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (if needed)
  • My changes generate no new warnings
  • I have tested on my Local Machine

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced new components: CompleteCourse, CompleteCourseCard, Footer, VideoCancelPage, VideoPayment, VideoThankYouPage, ClientEditProfilePage, ChefProfilePage, GroupPaymentPage, and others for enhanced user experience.
    • Added functionality for displaying completed courses and chef profiles.
    • Implemented dynamic payment processing and user feedback pages for payment success and failure.
    • Enhanced profile editing capabilities for chefs and clients.
  • Improvements

    • Enhanced navigation and user dropdown in the Navbar.
    • Improved form handling for sign-up and login processes, including better error handling and asynchronous operations.
    • Updated styling and layout across various components for better readability and responsiveness.
    • Dynamic data rendering for course and group details based on URL parameters.
  • Bug Fixes

    • Resolved issues in the logic for displaying upcoming workshops based on window size.
  • Chores

    • Updated dependencies in package.json and added .env to .gitignore for better environment management.

Copy link

coderabbitai bot commented Nov 23, 2024

Walkthrough

The pull request introduces several modifications across multiple components in the application. Key changes include updates to the Navbar component's navigation links, the addition of new components such as CompleteCourse, CompleteCourseCard, and Footer, and enhancements to the SignUp, Login, and payment-related components. The changes also involve the introduction of new properties in data types for courses and groups, as well as updates to the .gitignore and package.json files to include new dependencies. Overall, the changes aim to improve navigation, user interface, and data handling.

Changes

File Path Change Summary
app/chef/components/Navbar.tsx Updated navigation links and dropdown menu; added button for dropdown visibility; improved accessibility roles.
app/client/components/profile/CompleteCourse.tsx Introduced CompleteCourse component with a prop for limiting displayed courses.
app/client/components/profile/CompleteCourseCard.tsx Added CompleteCourseCard component to display individual course details.
app/client/components/sign-up/SignUp.tsx Updated handleSubmit to be async; renamed properties for consistency; added error handling.
app/client/profile/edit-profile/page.tsx Introduced ClientEditProfilePage component to manage profile editing layout.
app/client/profile/page.tsx Introduced ClientProfilePage component for main profile display.
package.json Added dependencies: @next/env, axios, cookie, js-cookie, and jsonwebtoken.
.gitignore Added .env to ignore local environment configuration files.
app/chef/components/Footer.tsx Introduced Footer component for application footer layout.
app/chef/components/login&forget-password/Login.tsx Updated login handling to be async; modified storage logic for "Remember me" feature.
app/chef/components/profile/ChefProfile.tsx Introduced ChefProfile component to display chef's profile information.
app/chef/components/profile/EditChefProfile.tsx Introduced EditChefProfile component for editing chef's profile details.
app/chef/components/sign-up-chef/SignUpChef.tsx Enhanced SignUpChef component to manage multiple input fields and handle submissions.
app/chef/profile/edit-profile/page.tsx Introduced EditChefProfilePage component for editing chef profiles.
app/chef/profile/page.tsx Introduced ChefProfilePage component for chef profile display.
app/client/components/group/payment/GroupPayment.tsx Updated payment handling to be async; modified onPaid function to accept dynamic URLs.
app/client/components/video/course-detail/CourseDetailFirstPage.tsx Updated button to navigate dynamically; removed unnecessary buttons.
app/client/components/video/payment/VideoCancelPage.tsx Introduced VideoCancelPage for handling payment failures.
app/client/components/video/payment/VideoPayment.tsx Introduced VideoPayment component for managing video payment transactions with validation.
app/client/components/video/payment/VideoThankYouPage.tsx Introduced VideoThankYouPage to display a thank-you message after purchase.
app/client/data/full-mock-data.ts Added ingredientPrice property to course objects in mock data.
app/client/types/courses.d.ts Updated Course interface to include ingredientPrice.
app/client/video/complete/page.tsx Introduced GroupCompletePage component to display thank-you page after video completion.
app/client/video/incomplete/page.tsx Introduced GroupCompletePage component to handle payment cancellation.
app/client/video/payment/[courseId]/page.tsx Introduced GroupPaymentPage to manage video payment layout.
app/unsigned/components/group/UpcomingWorkshopCard.tsx Modified logic for displaying workshop cards based on window size.
app/unsigned/types/group.d.ts Updated Group interface to include ingredientPrice.
app/client/components/my-learning/GroupCardEnrolled.tsx Updated link routing for group learning page.
app/client/components/my-learning/GroupPage.tsx Changed title from "Group Courses" to "Workshops".
app/client/components/my-learning/ProgressPage.tsx Changed title from "Video Courses" to "Courses".
app/client/components/video-player/VideoControl.tsx Updated review submission handling to be asynchronous with improved error management.
app/client/components/video/search/NewCourseCard.tsx Updated courses data source for rendering.
app/client/components/video/search/TopRateCourse.tsx Updated courses data source for rendering.
app/client/group/learning/[groupId]/page.tsx Introduced GroupLearningPage component with error boundary for error handling.
app/client/group/learning/page.tsx Removed GroupLearningPage component.

Possibly related PRs

Suggested labels

enhancement


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

netlify bot commented Nov 23, 2024

Deploy Preview for wecooked failed.

Name Link
🔨 Latest commit 1227111
🔍 Latest deploy log https://app.netlify.com/sites/wecooked/deploys/6756b05a7c517900085ed470

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

🧹 Outside diff range and nitpick comments (35)
app/client/profile/edit-profile/page.tsx (2)

1-2: Consider using path aliases for imports

Multiple parent directory traversals (../../) can make imports brittle to refactoring. Consider using TypeScript path aliases (e.g., @components/*) to make imports more maintainable and resistant to structural changes.

Example configuration in tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@components/*": ["app/components/*"]
    }
  }
}

Then imports would become:

import Navbar from '@components/Navbar'
import EditProfile from '@components/profile/EditProfile'

4-11: Consider adding error boundaries and loading states

The component should handle potential errors from child components and show appropriate loading states while data is being fetched.

Consider implementing:

  1. Error boundary wrapper for graceful error handling
  2. Loading states for asynchronous operations
  3. Suspense boundaries if using React Suspense

Would you like me to provide an example implementation with these features?

app/client/group/payment/[groupId]/page.tsx (1)

1-2: Consider using path aliases to improve import readability

The deep relative paths (../../../) make the imports harder to maintain. Consider configuring path aliases in your tsconfig.json/jsconfig.json to simplify these imports.

Example configuration and usage:

// tsconfig.json or jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/components/*": ["app/client/components/*"]
    }
  }
}

// In this file
-import Navbar from '../../../components/Navbar'
-import GroupPayment from '../../../components/group/GroupPayment'
+import Navbar from '@/components/Navbar'
+import GroupPayment from '@/components/group/GroupPayment'
app/client/profile/page.tsx (3)

7-11: Consider adding loading states and error boundaries.

The profile page should handle loading states and errors gracefully.

+import { Suspense, ErrorBoundary } from 'react'
+import LoadingSpinner from '../components/LoadingSpinner'
+import ErrorFallback from '../components/ErrorFallback'

 const ProfilePage: FC = () => {
   return (
     <main className="min-h-screen">
       <Navbar />
+      <ErrorBoundary FallbackComponent={ErrorFallback}>
+        <Suspense fallback={<LoadingSpinner />}>
           <Profile />
           <CompleteCourse />
+        </Suspense>
+      </ErrorBoundary>
     </main>
   )
 }

1-13: Consider implementing metadata for SEO.

As this is a Next.js page component, we should add metadata for better SEO.

+import { Metadata } from 'next'
+
+export const metadata: Metadata = {
+  title: 'Profile | WeCooked',
+  description: 'View and manage your WeCooked profile',
+}
+
 const ProfilePage: FC = () => {
   // ... rest of the component
 }

1-13: Consider shared components between client and chef profiles.

Since this PR implements both client and chef profile pages, consider extracting common functionality into shared components to avoid code duplication. Components like profile editing, course completion display, and basic profile information could potentially be shared between client and chef profiles with proper composition and prop interfaces.

Suggested structure:

app/
  shared/
    components/
      profile/
        BaseProfile.tsx
        BaseProfileEdit.tsx
        CourseList.tsx
  client/
    components/
      profile/
        ClientProfile.tsx (extends BaseProfile)
        ClientProfileEdit.tsx (extends BaseProfileEdit)
  chef/
    components/
      profile/
        ChefProfile.tsx (extends BaseProfile)
        ChefProfileEdit.tsx (extends BaseProfileEdit)
app/client/types/group.d.ts (1)

Line range hint 1-18: Improve interface organization and naming conventions.

The interface mixes different concerns (group, chef, pricing, etc.) and has inconsistent naming. Consider:

  1. Organizing related properties together
  2. Fixing the naming convention for groupNumberofparticipants
 export interface Group {
   // Group basic info
   groupId: number
   groupTitle: string
   groupDetail: string
   groupCategory: string
   groupStatus: boolean
   
   // Capacity and scheduling
-  groupNumberofparticipants: number
+  groupNumberOfParticipants: number
   groupDate: Date | string
   groupLinkZoom?: string
   
   // Media
   groupPicture: string
   
   // Pricing
   groupPrice: number
   ingredientPrice?: number
   
   // Chef information
   groupChefId?: number
   chefName: string
   chefImage: string
   
   // Additional info
   groupPackId?: string
   reviewRating: number
 }
app/client/components/profile/CompleteCourse.tsx (2)

12-13: Replace hardcoded margins with design tokens.

Using hardcoded margin values (mx-48) makes the component less maintainable and responsive.

Consider using design tokens or container classes:

-    <div className="flex flex-col justify-start pb-12 mx-48">
-      <h1 className="text-lg mt-8">Completed Course</h1>
+    <div className="flex flex-col justify-start pb-12 container mx-auto">
+      <h1 className="text-lg font-semibold mt-8">Completed Courses</h1>

15-17: Optimize list rendering for large datasets.

The current implementation might have performance issues with large datasets. Consider implementing virtualization for better performance.

Consider using a virtualized list component:

-        {displayedCourses.map((course) => (
-          <CompleteCourseCard key={course.courseId} course={course} />
-        ))}
+        <VirtualizedList
+          data={displayedCourses}
+          renderItem={(course) => (
+            <CompleteCourseCard key={course.courseId} course={course} />
+          )}
+          itemHeight={200} // Adjust based on your card height
+        />
app/client/group/learning/page.tsx (2)

13-21: Improve semantic HTML structure and accessibility.

The sections lack semantic meaning and styling classes. Consider adding appropriate ARIA labels and styling classes for better accessibility and maintainability.

-        <section>
+        <section 
+          className="py-8"
+          aria-label="Group Learning Content"
+        >
           <GroupLearning />
         </section>
-        <section>
+        <section 
+          className="py-8 bg-gray-50"
+          aria-label="Group Learning Curve"
+        >
           <GroupCurvePage />
         </section>
-        <section>
+        <section 
+          className="py-8"
+          aria-label="Frequently Asked Questions"
+        >
           <FAQPage />
         </section>

8-8: Add TypeScript type annotations.

Consider adding type annotations to the component for better maintainability and type safety.

-export default function GroupLearningPage() {
+export default function GroupLearningPage(): React.ReactElement {
app/client/components/group/GroupCurvePage.tsx (2)

1-3: Add TypeScript interface and component documentation.

Consider adding TypeScript interface and JSDoc documentation to improve maintainability and code clarity.

 import Image from 'next/image'

+/**
+ * GroupCurvePage component displays a Zoom workshop join page with background image and join button.
+ */
+interface GroupCurvePageProps {}
+
-export default function GroupCurvePage() {
+export default function GroupCurvePage({}: GroupCurvePageProps) {

3-31: Consider architectural improvements for Zoom integration.

The component needs additional architectural considerations:

  1. Integration with Zoom SDK or API
  2. Error boundary implementation
  3. Loading states management
  4. Environment configuration for Zoom credentials

Consider implementing a custom hook for Zoom integration:

// hooks/useZoomMeeting.ts
interface UseZoomMeetingProps {
  meetingId: string;
  password?: string;
}

export const useZoomMeeting = ({ meetingId, password }: UseZoomMeetingProps) => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const joinMeeting = async () => {
    setIsLoading(true);
    try {
      // TODO: Implement Zoom SDK integration
    } catch (err) {
      setError(err as Error);
    } finally {
      setIsLoading(false);
    }
  };

  return { joinMeeting, isLoading, error };
};
app/client/components/video/course-detail/MeetChefCurvePage.tsx (1)

Line range hint 1-38: Consider these maintainability improvements.

  1. Extract text content to a separate constants file for easier maintenance and potential internationalization
  2. Define image paths as constants
  3. Add TypeScript interface for component props
  4. Simplify the nested div structure using grid/flex layout

Here's a suggested refactor:

 import React from 'react'
 import Image from 'next/image'
+
+interface ChefProfile {
+  name: string;
+  specialty: string;
+  description: string;
+  image: string;
+  backgroundImage: string;
+}
+
+const CHEF_PROFILE: ChefProfile = {
+  name: 'Chef Sieng',
+  specialty: 'Master of Italian Cuisine',
+  description: 'Chef Sieng is a culinary expert specializing in authentic Italian dishes. With years of experience, he combines rich flavors and fresh ingredients to create delightful culinary experiences. Join him to explore the art of Italian cooking and master classic recipes!',
+  image: '/images/chef_tutorial.png',
+  backgroundImage: '/svg/RedBG.svg'
+};

 export default function MeetChefCurvePage() {
   return (
-    <div className="relative w-full flex flex-col items-center">
+    <div className="relative grid grid-cols-12 items-center">
       <Image
-        src="/svg/RedBG.svg"
+        src={CHEF_PROFILE.backgroundImage}
         alt="Red Background"
         width={1100}
         height={1100}
         className="w-full h-auto"
       />
-      <div>
-        <div className="absolute w-5/12 left-20 z-10 max-w-2xl">
+      <div className="col-span-5 col-start-2 z-10">
           <h1 className="py-1.5 text-5xl font-bold text-white inline-block text-transparent bg-clip-text">
-            Meet Chef Sieng: Master of Italian Cuisine
+            Meet {CHEF_PROFILE.name}: {CHEF_PROFILE.specialty}
           </h1>
           <p className="text-xl max-w-xl pt-12 text-white">
-            Chef Sieng is a culinary expert specializing in authentic Italian
-            dishes. With years of experience, he combines rich flavors and fresh
-            ingredients to create delightful culinary experiences. Join him to
-            explore the art of Italian cooking and master classic recipes!
+            {CHEF_PROFILE.description}
           </p>
-        </div>
-        <div className="absolute right-16 top-[168px] z-10 w-1/2 md:w-auto">
+      </div>
+      <div className="col-span-5 col-start-7 z-10">
           <Image
-            src="/images/chef_tutorial.png"
+            src={CHEF_PROFILE.image}
             alt="Cooked Image"
             width={750}
             height={750}
             className="object-contain w-full h-auto"
           />
-        </div>
       </div>
     </div>
   )
 }
app/client/components/group/GroupThankYouPage.tsx (3)

1-5: Add TypeScript interface for future extensibility

Consider adding a props interface to make the component more maintainable and type-safe for future extensions.

 'use client'
 import Link from 'next/link'
 import { CheckIcon } from '@heroicons/react/16/solid'
 
+interface GroupThankYouPageProps {
+  // Add props as needed, e.g.:
+  // customTitle?: string;
+  // redirectPath?: string;
+}
+
-export default function GroupThankYouPage() {
+export default function GroupThankYouPage({}: GroupThankYouPageProps) {

9-14: Extract gradient colors for better maintainability

Consider extracting the gradient colors to theme constants for consistency and easier maintenance across the application.

+// Add to a theme constants file
+const GRADIENT_COLORS = {
+  from: '#F0725C',
+  to: '#FE3511',
+} as const;
+
 // In component
-            <div className="absolute inset-0 bg-gradient-to-b from-[#F0725C] to-[#FE3511] rounded-full"></div>
+            <div className={`absolute inset-0 bg-gradient-to-b from-[${GRADIENT_COLORS.from}] to-[${GRADIENT_COLORS.to}] rounded-full`}></div>

32-36: Extract button component for reusability

The gradient button could be reused across the application. Consider creating a shared button component.

+// components/common/GradientButton.tsx
+interface GradientButtonProps {
+  href: string;
+  children: React.ReactNode;
+}
+
+export function GradientButton({ href, children }: GradientButtonProps) {
+  return (
+    <Link href={href}>
+      <div className="px-16 py-3 text-white cursor-pointer bg-gradient-to-b from-[#F0725C] to-[#FE3511] rounded-full font-medium transition-all duration-300 transform hover:scale-105 hover:shadow-lg">
+        {children}
+      </div>
+    </Link>
+  );
+}
+
+// In GroupThankYouPage
-        <Link href="/">
-          <div className="px-16 py-3 text-white cursor-pointer bg-gradient-to-b from-[#F0725C] to-[#FE3511] rounded-full font-medium transition-all duration-300 transform hover:scale-105 hover:shadow-lg">
-            Home
-          </div>
-        </Link>
+        <GradientButton href="/">
+          Home
+        </GradientButton>
app/client/components/group/GroupLearning.tsx (1)

15-45: Enhance accessibility and semantic structure.

While the layout looks visually appealing, there are opportunities to improve accessibility and semantic structure:

  1. Consider adding ARIA labels for the gradient text sections
  2. The instructor section could use semantic HTML elements

Consider these improvements:

       <div className="absolute right-20 top-44 z-10 max-w-[45%]">
-        <h3 className="py-1.5 text-2xl font-bold bg-gradient-to-b from-[#F0725C] to-[#FE3511] inline-block text-transparent bg-clip-text">
+        <h3 className="py-1.5 text-2xl font-bold bg-gradient-to-b from-[#F0725C] to-[#FE3511] inline-block text-transparent bg-clip-text" aria-label="Workshop Date">
           November 15, 2024
         </h3>
         <!-- Similar aria-label additions for other gradient text -->
-        <div className="flex items-center pt-8">
+        <section className="flex items-center pt-8" aria-label="Instructor Information">
           <div className="flex items-center rounded-full w-11 h-11">
             <Image
               src="/images/chef.png"
               alt="Profile picture of chef"
               width={50}
               height={50}
               className="rounded-full"
             />
           </div>
-          <p className="px-2 text-lg">Instructor:</p>
-          <p className="text-lg underline underline-offset-4">
+          <span className="px-2 text-lg">Instructor:</span>
+          <span className="text-lg underline underline-offset-4">
             Chief Sieng Utahnamnon
-          </p>
-        </div>
+          </span>
+        </section>
app/client/components/profile/CompleteCourseCard.tsx (4)

19-26: Add loading and error handling for images.

The Image components should include loading behavior and error handling for better user experience.

 <Image
   src={course.courseImage}
   alt={course.courseTitle}
   fill
   className="object-cover"
+  loading="lazy"
+  onError={(e) => {
+    e.currentTarget.src = '/placeholder-course-image.jpg'
+  }}
 />

Also applies to: 55-62


64-64: Add aria-label for star rating.

The StarRating component should include an accessible label for screen readers.

-                <StarRating reviewRating={course.reviewRating} />
+                <StarRating 
+                  reviewRating={course.reviewRating}
+                  aria-label={`Course rated ${course.reviewRating} out of 5 stars`}
+                />

36-38: Extract repeated className strings into reusable styles.

The dietary and category tags share identical styling. Consider extracting these into a shared class or styled component.

+const tagStyles = "px-2 py-0.5 border-[1px] border-[#FE3511] rounded-xl font-semibold bg-gradient-to-b from-[#F0725C] to-[#FE3511] inline-block text-transparent bg-clip-text transition-all duration-300 transform"

-className="px-2 py-0.5 border-[1px] border-[#FE3511] rounded-xl font-semibold bg-gradient-to-b from-[#F0725C] to-[#FE3511] inline-block text-transparent bg-clip-text transition-all duration-300 transform"
+className={tagStyles}

Also applies to: 41-48


15-16: Add keyboard interaction support.

The Link component should have proper keyboard interaction support for better accessibility.

-    <Link href={`/client/my-learning/${course.courseId}`}>
+    <Link 
+      href={`/client/my-learning/${course.courseId}`}
+      className="block focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#FE3511] rounded-xl"
+    >
app/chef/components/Navbar.tsx (1)

60-64: Consider extracting navigation items to a constant.

While the navigation links are clean and well-structured, consider extracting them to a constant array for better maintainability and reusability.

+const navItems = [
+  { href: '/chef/video', label: 'Course' },
+  { href: '/chef/group', label: 'Workshop' },
+  { href: '/chef/tutorial', label: 'Tutorial' },
+];

 <>
-  <NavLink href="/chef/video">Course</NavLink>
-  <NavLink href="/chef/group">Workshop</NavLink>
-  <NavLink href="/chef/tutorial">Tutorial</NavLink>
+  {navItems.map(({ href, label }) => (
+    <NavLink key={href} href={href}>{label}</NavLink>
+  ))}
 </>
app/client/components/sign-up/SignUp.tsx (3)

30-32: LGTM! Consider removing unnecessary empty line.

The async function signature is appropriate for handling the API call. However, consider removing the empty line after preventDefault() for better code consistency.

  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault()
-
    try {

39-45: Enhance error handling by parsing the error response.

The API integration looks good, but consider parsing the error response from the server for better error handling.

  const response = await fetch('/api/signup', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(clientData),
  })
+  const data = await response.json()

51-54: Enhance user experience with loading state and success feedback.

Consider adding loading state during form submission and showing a success message before redirecting.

+ const [isLoading, setIsLoading] = useState(false)

  const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault()
+   setIsLoading(true)
    try {
      // ... existing code ...
+     // Show success message
+     toast.success('Registration successful! Redirecting...')
+     // Add slight delay for user to see success message
+     await new Promise(resolve => setTimeout(resolve, 1500))
      router.push('/client/login')
    } catch (error) {
      console.error('Registration failed:', error)
+     toast.error(error.message)
+   } finally {
+     setIsLoading(false)
    }
  }
app/client/components/profile/Profile.tsx (1)

5-28: Improve responsive design for mobile devices.

The current layout uses fixed margins and may not work well on mobile devices.

-<div className="mt-6 mx-48">
+<div className="mt-6 mx-4 md:mx-12 lg:mx-48">
   <div className="flex justify-between items-center">
-    <div className="flex gap-x-8 items-center">
+    <div className="flex flex-col md:flex-row gap-4 md:gap-x-8 items-center">
app/client/components/profile/EditProfile.tsx (1)

19-31: Improve accessibility and user feedback for profile image editing.

The profile image section needs improvements:

  • Hard-coded image path should be dynamic
  • Missing aria-label for the edit button
  • No visual or interactive feedback for the edit functionality

Consider these improvements:

 <Image
-  src="/images/profile.jpg"
+  src={userProfileImage || '/images/default-profile.jpg'}
   alt="User profile image"
   width={500}
   height={500}
 />
 
-<div className="absolute bottom-1 right-[470px] flex justify-center items-center rounded-full w-9 h-9 bg-[#F2F4F8] border border-[#C1C7CD]">
+<button
+  type="button"
+  onClick={handleImageEdit}
+  aria-label="Edit profile picture"
+  className="absolute bottom-1 right-[470px] flex justify-center items-center rounded-full w-9 h-9 bg-[#F2F4F8] border border-[#C1C7CD] hover:bg-gray-200 transition-colors"
+>
   <PencilIcon className="text-[#C1C7CD] w-6 h-6" />
-</div>
+</button>
app/client/data/group-course.ts (1)

Line range hint 9-275: Review pricing structure consistency

The relationship between groupPrice and ingredientPrice seems inconsistent:

  • Some expensive courses (groupPrice: 550) have low ingredient costs (ingredientPrice: 10)
  • Some cheaper courses (groupPrice: 350) have higher ingredient costs (ingredientPrice: 30)

Consider:

  1. Documenting the pricing strategy
  2. Implementing a pricing calculator that validates the relationship between course price and ingredient cost
  3. Adding business rules to ensure pricing consistency across different course categories
🧰 Tools
🪛 Biome (1.9.4)

[error] 6-6: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

app/client/components/Navbar.tsx (3)

31-36: Consider aligning route paths with their corresponding labels

The navigation items array improves maintainability, but there are inconsistencies between labels and their corresponding paths:

  • "Course" links to "/video"
  • "Workshop" links to "/group"

This could lead to confusion during maintenance.

Consider updating the paths to match their labels:

 const navItems = [
   { label: 'Home', path: '/home' },
   { label: 'My Learning', path: '/my-learning' },
-  { label: 'Course', path: '/video' },
-  { label: 'Workshop', path: '/group' },
+  { label: 'Course', path: '/course' },
+  { label: 'Workshop', path: '/workshop' },
 ]

121-125: Consider making the path prefix configurable

The current implementation hardcodes the '/client' prefix in the mapping. This could make it difficult to reuse the navigation structure for different user types (e.g., chefs).

Consider moving the prefix to the navItems configuration or making it a prop:

-const navItems = [
+const navItems = {
+  prefix: '/client',
+  items: [
   { label: 'Home', path: '/home' },
   { label: 'My Learning', path: '/my-learning' },
   { label: 'Course', path: '/video' },
   { label: 'Workshop', path: '/group' },
-]
+]}

// In JSX:
-{navItems.map(({ label, path }) => (
+{navItems.items.map(({ label, path }) => (
-  <NavLink key={path} href={`/client${path}`}>
+  <NavLink key={path} href={`${navItems.prefix}${path}`}>
     {label}
   </NavLink>
))}

131-132: Ensure path naming consistency for unauthenticated routes

While the labels have been updated to "Course" and "Workshop", the corresponding paths still use the old terminology (/unsigned/video and /unsigned/group).

Consider updating the paths to match the new terminology:

-<NavLink href="/unsigned/video">Course</NavLink>
-<NavLink href="/unsigned/group">Workshop</NavLink>
+<NavLink href="/unsigned/course">Course</NavLink>
+<NavLink href="/unsigned/workshop">Workshop</NavLink>
app/client/components/group/GroupPayment.tsx (3)

46-46: Remove console.log statements from production code

The console.log statement may expose sensitive information. It's best to remove it or conditionally include it only in development environments.

Apply this diff to remove the log:

- console.log(JSON.stringify(data))

114-124: Review required attribute on a read-only input field

The delivery date input is marked as required but is readOnly. Users cannot input a value manually, which may lead to validation issues.

Consider handling the requirement in your form logic or adjusting the input:

<input
  className="..."
  placeholder="Select a date"
- required
  readOnly
  value={...}
/>

Ensure that your form validation checks if a date has been selected.


138-141: Include addresses in form data to capture user input

The 'Shipping Address' and 'Billing Address' fields are not registered with react-hook-form, so their values won't be submitted. If you need this data, consider registering these fields.

For the 'Shipping Address':

<textarea
  className="..."
  placeholder="Enter Your Shipping address"
  required
+ {...register('shippingAddress')}
></textarea>

For the 'Billing Address':

<textarea
  className="..."
  placeholder="Enter your billing address"
  required
+ {...register('billingAddress')}
></textarea>

Also applies to: 210-214

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7c979a4 and 4163eb1.

⛔ Files ignored due to path filters (4)
  • package-lock.json is excluded by !**/package-lock.json
  • public/images/Aien.png is excluded by !**/*.png
  • public/images/juicy_beef.png is excluded by !**/*.png
  • public/svg/Bg_group_curve.svg is excluded by !**/*.svg
📒 Files selected for processing (20)
  • app/chef/components/Navbar.tsx (1 hunks)
  • app/client/components/Navbar.tsx (2 hunks)
  • app/client/components/group/GroupCurvePage.tsx (1 hunks)
  • app/client/components/group/GroupLearning.tsx (1 hunks)
  • app/client/components/group/GroupPayment.tsx (1 hunks)
  • app/client/components/group/GroupThankYouPage.tsx (1 hunks)
  • app/client/components/profile/CompleteCourse.tsx (1 hunks)
  • app/client/components/profile/CompleteCourseCard.tsx (1 hunks)
  • app/client/components/profile/EditProfile.tsx (1 hunks)
  • app/client/components/profile/Profile.tsx (1 hunks)
  • app/client/components/sign-up/SignUp.tsx (1 hunks)
  • app/client/components/video/course-detail/MeetChefCurvePage.tsx (1 hunks)
  • app/client/data/group-course.ts (17 hunks)
  • app/client/group/complete/page.tsx (1 hunks)
  • app/client/group/learning/page.tsx (1 hunks)
  • app/client/group/payment/[groupId]/page.tsx (1 hunks)
  • app/client/profile/edit-profile/page.tsx (1 hunks)
  • app/client/profile/page.tsx (1 hunks)
  • app/client/types/group.d.ts (2 hunks)
  • package.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • app/client/group/complete/page.tsx
🧰 Additional context used
📓 Learnings (4)
app/client/components/group/GroupLearning.tsx (1)
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/components/group/GroupLearning.tsx:15-45
Timestamp: 2024-11-16T20:48:57.204Z
Learning: In the `GroupLearning` component (`app/client/components/group/GroupLearning.tsx`), the data is already being handled elsewhere, so using hardcoded values is acceptable in this context.
app/client/components/group/GroupPayment.tsx (4)
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/components/group/GroupPayment.tsx:106-138
Timestamp: 2024-11-16T15:28:49.798Z
Learning: In `app/client/components/group/GroupPayment.tsx`, the payment input fields are currently using mocked data for testing purposes, so security measures for handling real credit card information are not required at this time.
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/components/group/GroupPayment.tsx:210-214
Timestamp: 2024-11-17T16:25:23.385Z
Learning: In the `GroupPayment` component (`app/client/components/group/GroupPayment.tsx`), validation for the billing address field is not required in the payment form schema, as per the user's preference.
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/components/group/GroupPayment.tsx:54-67
Timestamp: 2024-11-15T20:55:46.859Z
Learning: In `app/client/components/group/GroupPayment.tsx`, the developer prefers to keep the current implementation of the delivery toggle button without additional accessibility enhancements.
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/group/payment/[groupId]/page.tsx:4-11
Timestamp: 2024-11-17T16:25:46.472Z
Learning: In `app/client/group/payment/[groupId]/page.tsx`, it is acceptable to omit TypeScript types and not utilize the `groupId` route parameter in the `GroupPaymentPage` component.
app/client/group/learning/page.tsx (1)
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/group/learning/page.tsx:7-7
Timestamp: 2024-11-17T16:05:40.060Z
Learning: In `app/client/group/learning/page.tsx`, the `FAQPage` component is correctly imported from `../../components/FAQPage`.
app/client/group/payment/[groupId]/page.tsx (1)
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/group/payment/[groupId]/page.tsx:4-11
Timestamp: 2024-11-17T16:25:46.472Z
Learning: In `app/client/group/payment/[groupId]/page.tsx`, it is acceptable to omit TypeScript types and not utilize the `groupId` route parameter in the `GroupPaymentPage` component.
🪛 Biome (1.9.4)
app/client/data/group-course.ts

[error] 6-6: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

🔇 Additional comments (15)
app/client/profile/edit-profile/page.tsx (1)

7-8: Verify Navbar state handling in edit profile context

Ensure that the Navbar component correctly reflects the edit profile context and maintains proper navigation state.

✅ Verification successful

Navbar state handling is properly implemented in edit profile context

The Navbar component correctly handles profile-related state and navigation:

  • It maintains authentication state through both context and local storage
  • Includes proper profile navigation in the user dropdown menu
  • Has a direct link to "/client/profile" that integrates with the edit profile flow
  • EditProfile component properly handles its own local state and includes correct navigation back to profile
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if Navbar component handles profile-related states
ast-grep --pattern 'interface NavbarProps {
  $$$
}'

# Check for profile-related routing logic
rg -A 5 'useRouter|navigation' app/components/Navbar.tsx

Length of output: 179


Script:

#!/bin/bash
# Let's first locate the Navbar component file
fd -t f -e tsx -e ts -e jsx -e js Navbar

# Then check for any profile-related routing or state management
rg -l "profile|Profile" $(fd -t f -e tsx -e ts -e jsx -e js Navbar)

# Also search for navigation logic in profile-related files
rg -l "useRouter|useNavigation|navigation" $(fd -t f -e tsx -e ts -e jsx -e js profile)

Length of output: 587


Script:

#!/bin/bash
# Let's examine the client Navbar implementation since we're in the client profile context
cat app/client/components/Navbar.tsx

# Also check the EditProfile component implementation
cat app/client/components/profile/EditProfile.tsx

# Check for any state sharing or context between these components
rg -A 5 "useState|useContext|createContext" app/client/components/Navbar.tsx app/client/components/profile/EditProfile.tsx

Length of output: 18731

app/client/group/payment/[groupId]/page.tsx (1)

4-11: Verify consistent payment page implementation across the application

Let's ensure this payment page structure is consistent with other payment-related pages in the application.

✅ Verification successful

Page structure follows consistent pattern across the application

The implementation of the payment page follows the same consistent pattern used throughout the application:

  • All pages use the <Navbar /> component at the top
  • Pages are structured with a root <div> containing the navbar and their main component
  • The GroupPayment component is only used in this specific payment flow

This implementation aligns with the established patterns in the codebase, as seen in similar pages like:

  • app/client/profile/page.tsx
  • app/client/profile/edit-profile/page.tsx
  • app/client/group/learning/page.tsx
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for other payment-related pages and GroupPayment component usage

# Find all payment-related pages
echo "Finding payment-related pages:"
fd --type f "payment.*\.(tsx|jsx|js|ts)$" app/

# Check GroupPayment component usage
echo -e "\nChecking GroupPayment component usage:"
rg -l "GroupPayment" --type ts --type tsx

Length of output: 327


Script:

#!/bin/bash
# Let's try a different approach to find payment-related pages and GroupPayment usage

# Find all payment-related pages and components
echo "Finding payment-related files:"
find . -type f -regex ".*payment.*\.[jt]sx?" -not -path "*/node_modules/*" -not -path "*/dist/*"

# Check GroupPayment component usage with ripgrep
echo -e "\nChecking GroupPayment component usage:"
rg "GroupPayment" --type-add 'tsx:*.tsx' --type-add 'ts:*.ts'

# Look for similar payment page structures
echo -e "\nChecking for similar page structures with Navbar:"
rg -A 5 "export default function.*Page.*{.*\n.*return.*\n.*<div.*\n.*<Navbar" --type-add 'tsx:*.tsx'

Length of output: 1126


Script:

#!/bin/bash
# Let's check for similar page structures with a corrected regex

# Look for similar page structures with Navbar
echo "Checking page structures:"
rg -A 5 "export default function.*Page" --type-add 'tsx:*.tsx'

# Check if there are other payment components
echo -e "\nChecking for other payment-related components:"
rg "export default function.*Payment" --type-add 'tsx:*.tsx'

# Check if GroupPayment is imported elsewhere
echo -e "\nChecking GroupPayment imports:"
rg "import.*GroupPayment" --type-add 'tsx:*.tsx'

Length of output: 30163

app/client/profile/page.tsx (1)

9-10: Verify props requirements for Profile and CompleteCourse components.

The Profile and CompleteCourse components are rendered without any props. Let's verify if they require any mandatory props.

✅ Verification successful

Props verification completed - Profile component doesn't require props, CompleteCourse has optional prop

The verification shows:

  • Profile component is correctly used without props as it doesn't define any required props
  • CompleteCourse component has an optional limit prop with a default value of 5, so it can be used without props
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if Profile and CompleteCourse components require any props

# Check Profile component implementation
ast-grep --pattern 'interface $interface {
  $$$
}' ../client/components/profile/Profile.tsx

# Check CompleteCourse component implementation
ast-grep --pattern 'interface $interface {
  $$$
}' ../client/components/profile/CompleteCourse.tsx

Length of output: 379


Script:

#!/bin/bash
# First, let's find the correct paths for these components
fd -e tsx -e ts "Profile.tsx|CompleteCourse.tsx"

# Then let's check for any Props interface or type definitions in these files
rg -A 5 "interface.*Props|type.*Props" --type ts

Length of output: 7339


Script:

#!/bin/bash
# Let's check the implementation of Profile and CompleteCourse components
cat app/client/components/profile/Profile.tsx
cat app/client/components/profile/CompleteCourse.tsx

Length of output: 6400

app/client/types/group.d.ts (1)

Line range hint 1-18: Verify the impact of the new property.

Since this interface is being modified as part of implementing the client/chef profile pages, we should verify how this new property is being used.

✅ Verification successful

The ingredientPrice property is properly integrated and used across the codebase

The property is being used consistently:

  • It's defined in the Group interface and properly used in mock data (app/client/data/group-course.ts)
  • It's actively used in the payment component (GroupPayment.tsx) for calculating add-ons
  • The property is imported and referenced across multiple components in client, chef, and unsigned sections
  • The type is consistent with similar properties in other interfaces (e.g., ingredient.d.ts)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of Group interface and ingredientPrice property

# Find all TypeScript/JavaScript files that import or use the Group interface
echo "Files importing or using Group interface:"
rg -l "Group['\"]|Group>|Group\[" --type ts --type tsx

# Find specific usage of ingredientPrice
echo -e "\nUsage of ingredientPrice:"
rg "ingredientPrice" --type ts --type tsx -A 2

Length of output: 321


Script:

#!/bin/bash
# Let's try without file type restrictions since tsx wasn't recognized
# First, find files containing Group interface usage
echo "Files referencing Group:"
rg -l "Group['\"]|Group>|Group\["

# Then check usage of ingredientPrice
echo -e "\nUsage of ingredientPrice:"
rg "ingredientPrice" -A 2

# Also check for any files importing from the types directory
echo -e "\nImports from types directory:"
rg -l "from ['\"].*types/group"

Length of output: 4818

app/client/group/learning/page.tsx (2)

1-7: LGTM! Imports are well-organized and necessary.

All imports are being used in the component and follow consistent relative path patterns.


3-7: Verify the implementation of imported components.

Let's ensure all imported components are properly implemented and available.

✅ Verification successful

All imported components are properly implemented and available

All the imported components exist at their expected locations:

  • app/client/components/Navbar.tsx
  • app/client/components/group/GroupLearning.tsx
  • app/client/components/group/GroupCurvePage.tsx
  • app/client/components/Footer.tsx
  • app/client/components/FAQPage.tsx

The import paths in the code correctly match the actual file locations relative to the importing file.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence and implementation of imported components

# Check for the existence of imported components
echo "Checking for component files..."
for component in "Navbar" "GroupLearning" "GroupCurvePage" "Footer" "FAQPage"; do
  echo "Looking for $component..."
  fd -e tsx -e ts -e js -e jsx "$component\.tsx?$" app/client/
done

# Check GroupLearning implementation for required props
echo "Checking GroupLearning implementation..."
ast-grep --pattern 'export $_ function GroupLearning($_) {
  $$$
}'

Length of output: 1434

app/client/components/video/course-detail/MeetChefCurvePage.tsx (1)

15-15: Verify layout after removing top offset.

The removal of the top-56 class might affect the vertical alignment between the text and image sections. While the image maintains its explicit top positioning (top-[168px]), the text section now lacks vertical positioning.

Let's verify the layout consistency across different screen sizes:

Consider either:

  1. Restoring the top offset for consistent positioning
  2. Removing explicit top positioning from both sections and using flex/grid layout
✅ Verification successful

Let me gather more context about the layout structure and responsive design patterns in this component.


Removal of top-56 class is intentional and safe

The layout structure shows that the component uses a flex container as the parent, and the removal of top-56 is actually an improvement:

  • The main wrapper div uses flex flex-col items-center which provides proper alignment
  • Both text and image sections are absolutely positioned relative to their parent
  • The image section maintains its specific positioning (top-[168px]) for precise alignment
  • The text section's positioning (left-20) works with the flex container to achieve the desired layout
  • Responsive classes (md:w-auto) are properly maintained for the image section
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for responsive design breakpoints and potential layout issues

# Search for responsive classes and breakpoints
rg -A 2 'sm:|md:|lg:|xl:|2xl:' app/client/components/video/course-detail/

# Look for similar layout patterns in other components
ast-grep --pattern 'div className="$_absolute$_"'

Length of output: 766


Script:

#!/bin/bash
# Get the full content of MeetChefCurvePage.tsx to understand the layout structure
cat app/client/components/video/course-detail/MeetChefCurvePage.tsx

# Check for flex/grid layout patterns in the component
rg -A 2 'flex|grid' app/client/components/video/course-detail/MeetChefCurvePage.tsx

Length of output: 1586

app/client/components/group/GroupLearning.tsx (1)

1-2: LGTM! Clean and minimal imports.

The Next.js Image component is appropriately imported for optimized image loading.

app/client/components/profile/CompleteCourseCard.tsx (2)

1-9: LGTM! Clean imports and well-defined interface.

The imports are appropriate and the interface is properly typed with the Course type.


11-13: LGTM! Clean component definition.

The component follows React functional component patterns with proper props destructuring.

app/chef/components/Navbar.tsx (1)

Line range hint 1-131: Verify integration with new profile pages.

Since this PR implements client/chef profile pages, let's verify that all profile-related routes and components are properly integrated.

✅ Verification successful

Let me check the actual profile route implementation and component structure to ensure proper integration.


Profile routes and components are properly integrated

The verification shows consistent profile route implementation across the codebase:

  • All navbar components (chef, client, unsigned) correctly link to /client/profile
  • Profile components exist in the client directory with proper navigation:
    • Profile component with edit route (/client/profile/edit-profile)
    • EditProfile component with navigation back to profile (/client/profile)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for the existence of profile page components and their imports
echo "Checking for profile page components..."
fd -e tsx -e ts "profile\.tsx$" app/chef/
fd -e tsx -e ts "profile\.tsx$" app/client/

# Verify route consistency
echo "Checking route consistency..."
rg -l "href=.*profile" app/

Length of output: 552


Script:

#!/bin/bash
# Check the profile route implementations and their content
echo "Checking profile routes and components..."
rg -A 5 "href=.*profile" app/chef/components/Navbar.tsx app/client/components/profile/

# Check if there are any mismatches in route paths
echo "Checking route path consistency..."
rg -A 2 "href=.*profile" app/

Length of output: 3946

app/client/components/profile/EditProfile.tsx (1)

1-6: LGTM! Imports and setup are properly configured.

The necessary imports are present and the 'use client' directive is correctly placed for client-side rendering.

app/client/data/group-course.ts (1)

Line range hint 9-275: Verify data consistency and add type constraints

Several observations about the group data:

  1. All groups use the same Zoom link which could cause confusion
  2. Some course titles don't match their categories (e.g., "Italian Tapas Experience" is unusual as tapas are Spanish)
  3. The new ingredientPrice field lacks type constraints

Consider adding these improvements:

  1. Define minimum/maximum constraints for ingredientPrice in the Group type:
interface Group {
  // ... other fields
  ingredientPrice: number & { min: 0, max: 100 } // Using branded types for constraints
}
  1. Generate unique Zoom links for each group
  2. Review and align course titles with their respective cuisine categories
🧰 Tools
🪛 Biome (1.9.4)

[error] 6-6: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

app/client/components/Navbar.tsx (1)

119-119: Review authentication state check logic

The current authentication check uses a logical OR condition with localStorage:

authcontextvalue?.isAuthenticated || authLocalState == 'true'

This could potentially lead to a security issue if the context is not properly initialized but localStorage contains a stale 'true' value.

Let's verify the authentication handling across the codebase:

Also applies to: 127-127

✅ Verification successful

Let me gather more information about the authentication context and state management.


Let me try one more verification to understand the auth context implementation:


Let me try one final verification with corrected command syntax:


Authentication state check is implemented correctly

The current implementation is secure because:

  1. The auth context is properly initialized through AuthProvider at the root layout
  2. The localStorage auth state is synchronized with the context through proper initialization in useEffect
  3. Logout properly cleans up both context and localStorage states
  4. The OR condition is intentional to maintain auth state across page refreshes

The pattern is consistently implemented across all navbar variants (client, chef, unsigned) with proper cleanup on logout.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar authentication checks that might have the same issue
rg -A 2 "authLocalState.*==.*'true'" 

# Look for authentication state management
ast-grep --pattern 'localStorage.setItem($ITEM, $VALUE)' 

Length of output: 2418


Script:

#!/bin/bash
# Search for authentication context initialization and usage
ast-grep --pattern 'const $AUTH = useAuth()'

# Look for localStorage getItem for auth state
ast-grep --pattern 'localStorage.getItem("isAuthenticated")'

# Search for auth state removal/logout
ast-grep --pattern 'localStorage.removeItem($ITEM)'

# Look for the auth provider implementation
rg -l "AuthProvider" --type ts --type tsx

Length of output: 488


Script:

#!/bin/bash
# Search for auth context/provider implementation with proper file extensions
rg -l "AuthProvider" -t ts -t tsx

# Look for useAuth hook implementation
rg -l "useAuth" -t ts -t tsx

# Search for auth context initialization
rg "createContext.*Auth" -t ts -t tsx

# Look for how authLocalState is initialized
rg "authLocalState.*localStorage" -t ts -t tsx -A 3

Length of output: 268


Script:

#!/bin/bash
# Search for auth context/provider and hook implementation
rg "AuthProvider|useAuth|createContext.*Auth"

# Look for how authLocalState is initialized and used
rg "const.*authLocalState" -A 2

Length of output: 2758

app/client/components/group/GroupPayment.tsx (1)

13-22: Validation schema correctly defines payment fields

The paymentSchema is well-defined using zod, ensuring robust validation for the payment fields.

app/client/profile/edit-profile/page.tsx Outdated Show resolved Hide resolved
app/client/group/payment/[groupId]/page.tsx Outdated Show resolved Hide resolved
app/client/profile/page.tsx Outdated Show resolved Hide resolved
app/client/profile/page.tsx Outdated Show resolved Hide resolved
app/client/types/group.d.ts Outdated Show resolved Hide resolved
app/client/data/group-course.ts Outdated Show resolved Hide resolved
app/client/components/group/GroupPayment.tsx Outdated Show resolved Hide resolved
app/client/components/group/GroupPayment.tsx Outdated Show resolved Hide resolved
app/client/components/group/GroupPayment.tsx Outdated Show resolved Hide resolved
app/client/components/group/GroupPayment.tsx Outdated Show resolved Hide resolved
@tong788 tong788 self-assigned this Nov 23, 2024
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: 53

🧹 Outside diff range and nitpick comments (39)
app/client/video/incomplete/page.tsx (1)

1-6: Add TypeScript types and improve component documentation

Consider adding TypeScript types and JSDoc documentation to improve maintainability:

 import React from 'react'
 import VideoCancelPage from '../../components/video/payment/VideoCancelPage'
 
+/**
+ * Renders the video cancellation page for incomplete group sessions
+ */
+const VideoCancelPage: React.FC = () => {
-export default function GroupCompletePage() {
   return <VideoCancelPage />
 }
+
+export default VideoCancelPage
app/client/types/courses.d.ts (1)

Line range hint 1-12: Add documentation to clarify price components

The interface now has two price-related fields (coursePrice and ingredientPrice) but their purposes and differences aren't documented. Consider adding JSDoc comments to explain:

  • The purpose of each price component
  • Any validation constraints (e.g., non-negative values)
  • How they relate to the total cost

Example improvement:

 export interface Course {
+  /** Unique identifier for the course */
   courseId: number;
+  /** Title/name of the course */
   courseTitle: string;
+  /** Category/type of the course */
   courseCategory: string;
+  /** Price for the course service/instruction (must be >= 0) */
   coursePrice: number;
+  /** Price for the required ingredients (must be >= 0) */
   ingredientPrice: number;
   reviewRating: number;
   chefName: string;
   courseImage: string;
   chefImage: string;
   courseDietary?: string[];
 }
app/client/video/payment/[courseId]/page.tsx (1)

4-11: Consider enhancing the component structure

The component could benefit from:

  1. Utilizing the courseId route parameter to fetch and validate course data
  2. Adding error boundaries and loading states
  3. Adding TypeScript types for better type safety

Here's a suggested enhancement:

import { useParams } from 'next/navigation'
import ErrorBoundary from '../../../components/ErrorBoundary'

interface VideoPaymentPageProps {
  // Add props if needed
}

export default function VideoPaymentPage({}: VideoPaymentPageProps) {
  const { courseId } = useParams()

  return (
    <ErrorBoundary>
      <div>
        <Navbar />
        <VideoPayment courseId={courseId} />
      </div>
    </ErrorBoundary>
  )
}
app/chef/profile/page.tsx (1)

1-3: Consider enhancing the component structure and imports

  1. Consider using absolute imports with TypeScript path aliases for better maintainability
  2. The page component should be async by default following Next.js 13+ conventions
  3. Consider adding type definitions for the imported components
-import Navbar from '../components/Navbar'
-import ChefProfile from '../components/profile/ChefProfile'
-import Footer from '../components/Footer'
+import { Navbar } from '@/app/chef/components/Navbar'
+import { ChefProfile } from '@/app/chef/components/profile/ChefProfile'
+import { Footer } from '@/app/chef/components/Footer'

-export default function ChefProfilePage() {
+export default async function ChefProfilePage() {
app/unsigned/types/group.d.ts (3)

6-6: Consider adding JSDoc comments to document the price fields.

Please add documentation to clarify the distinction between ingredientPrice and groupPrice. This will help other developers understand the purpose of each price field.

  groupPrice: number
+ /** Price of ingredients required for this group session */
  ingredientPrice: number

Line range hint 1-17: Consider splitting the interface for better separation of concerns.

The Group interface currently mixes different concerns (group details, chef information, review data). Consider breaking this into smaller, focused interfaces:

interface Chef {
  id: number
  name: string
  image: string
}

interface GroupReview {
  rating: number
}

interface Group {
  groupId: number
  // ... group specific fields
  chef: Chef
  review: GroupReview
}

This would make the code more maintainable and follow the Single Responsibility Principle.


7-7: Fix naming inconsistency in groupNumberofparticipants.

The property groupNumberofparticipants doesn't follow the camelCase convention used in the rest of the interface.

- groupNumberofparticipants: number
+ groupNumberOfParticipants: number
app/client/profile/page.tsx (1)

7-12: Add semantic HTML and ARIA landmarks

The component should use semantic HTML structure with proper ARIA landmarks for better accessibility.

Consider adding semantic structure:

-    <>
+    <div role="main">
       <Navbar />
-      <ClientProfile />
-      <CompleteCourse />
+      <section aria-label="User Profile">
+        <ClientProfile />
+      </section>
+      <section aria-label="Completed Courses">
+        <CompleteCourse />
+      </section>
       <Footer />
-    </>
+    </div>
.gitignore (1)

28-30: Consider adding an .env.example template file.

While ignoring .env files is correct, consider adding an .env.example template file to help other developers set up their environment correctly.

Add this to your .gitignore:

# local env files
.env*.local
+
+# allow env example file
+!.env.example

.env

Also applies to: 37-37

app/client/components/video/payment/VideoCancelPage.tsx (2)

7-8: Optimize layout properties to prevent potential issues.

The layout structure has some redundant and potentially problematic properties:

  1. Both min-h-screen and h-screen are used - min-h-screen alone is sufficient
  2. The fixed margin (m-10) might cause horizontal scrolling on smaller screens

Consider this optimization:

-    <div className="flex justify-center items-center min-h-screen bg-gradient-to-b from-[#F0725C] to-[#FE3511] h-screen animate-fadeIn">
-      <div className="flex justify-center items-center flex-col bg-white rounded-lg shadow-lg p-24 w-full m-10 h-5/6">
+    <div className="flex justify-center items-center min-h-screen bg-gradient-to-b from-[#F0725C] to-[#FE3511] animate-fadeIn">
+      <div className="flex justify-center items-center flex-col bg-white rounded-lg shadow-lg p-8 md:p-24 w-full mx-4 md:mx-10 my-8 md:my-10">

9-14: Extract gradient colors into Tailwind theme or CSS variables.

The gradient colors are duplicated from the parent container. Consider extracting these values for better maintainability.

Add to your Tailwind config:

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
+     gradientColorStops: {
+       'primary': {
+         'from': '#F0725C',
+         'to': '#FE3511',
+       },
+     },
    },
  },
}

Then update the component:

-            <div className="absolute inset-0 bg-gradient-to-b from-[#F0725C] to-[#FE3511] rounded-full"></div>
+            <div className="absolute inset-0 bg-gradient-to-b from-primary-from to-primary-to rounded-full"></div>
app/client/components/video/payment/VideoThankYouPage.tsx (1)

9-14: Consider extracting magic numbers into CSS custom properties.

The icon dimensions and styling could be more maintainable by using CSS variables.

+    {/* Add to a global CSS file or style block */}
+    {/* :root {
+      --icon-size: 6rem;
+      --icon-gradient-start: #F0725C;
+      --icon-gradient-end: #FE3511;
+    } */}
     <div className="flex justify-center mb-3">
-      <div className="relative w-24 h-24 mb-10">
-        <div className="absolute inset-0 bg-gradient-to-b from-[#F0725C] to-[#FE3511] rounded-full"></div>
-        <CheckIcon className="w-24 h-24 text-white relative z-10" />
+      <div className="relative w-[var(--icon-size)] h-[var(--icon-size)] mb-10">
+        <div className="absolute inset-0 bg-gradient-to-b from-[var(--icon-gradient-start)] to-[var(--icon-gradient-end)] rounded-full"></div>
+        <CheckIcon className="w-[var(--icon-size)] h-[var(--icon-size)] text-white relative z-10" />
app/chef/components/Footer.tsx (1)

12-77: Enhance layout responsiveness and styling maintainability

The current layout uses hardcoded values and might not be responsive on smaller screens.

Consider these improvements:

- <div className="relative bg-white">
-   <div className="flex justify-between w-full p-8 px-28">
+ <footer className="relative bg-white">
+   <div className="container mx-auto px-4 md:px-6 lg:px-8">
+     <div className="flex flex-col md:flex-row justify-between items-center gap-6 py-8">

// Update footer elements container
- <div className="flex gap-x-6">
+ <nav className="flex flex-wrap justify-center gap-x-6 gap-y-2">

// Update social media container
- <div className="flex items-center gap-x-3">
+ <div className="flex items-center gap-x-3 mt-4 md:mt-0">

// Update copyright section
- <div className="flex justify-center pb-16">
-   Copyright ©{new Date().getFullYear()} Wecooked
- </div>
+ <div className="text-center py-4 border-t border-gray-200 mt-8">
+   <p className="text-sm text-gray-600">
+     © {new Date().getFullYear()} WeCooked. All rights reserved.
+   </p>
+ </div>
app/client/components/video/course-detail/CourseDetailFirstPage.tsx (1)

6-6: Consider improving component architecture and type safety

The current implementation could benefit from several architectural improvements:

  1. Extract hardcoded content into props or data fetching
  2. Add TypeScript interfaces for props
  3. Implement error boundaries for image loading failures

Consider restructuring the component like this:

interface CourseDetailProps {
  course: {
    id: string;
    title: string;
    description: string;
    instructor: {
      name: string;
      image: string;
    };
    cuisine: string;
    courseImage: string;
  };
  onEnrollClick: (courseId: string) => Promise<void>;
}

export default function CourseDetailFirstPage({ course, onEnrollClick }: CourseDetailProps) {
  // Component implementation
}

This would make the component:

  • Reusable across different courses
  • Type-safe with TypeScript
  • Easier to test and maintain
app/unsigned/components/group/UpcomingWorkshopCard.tsx (2)

9-15: Consider improving responsive initialization logic

The current implementation could be enhanced in several ways:

  1. Use a default state that matches the server-side render to prevent hydration mismatch
  2. Extract breakpoint values as named constants
  3. Align breakpoints with Tailwind's default values (640px is correct, but 1024px should be 1280px for xl)
+const SCREEN_SM = 640
+const SCREEN_XL = 1280
+
 const [cardsToShow, setCardsToShow] = useState<number>(() => {
-  if (typeof window !== 'undefined') {
-    if (window.innerWidth < 640) return 1
-    if (window.innerWidth < 1024) return 2
-  }
-  return 3
+  // Default to 3 to match SSR
+  return 3
 })

Line range hint 1-94: Consider enhancing component resilience and accessibility

While the core functionality is solid, consider these architectural improvements:

  1. Add error boundaries to handle potential data loading failures
  2. Implement loading states for better UX
  3. Enhance accessibility by:
    • Adding aria-labels to navigation buttons
    • Including keyboard navigation support
    • Adding role="region" with aria-label for the carousel
app/client/components/sign-up/SignUp.tsx (1)

16-18: Maintain consistent naming convention throughout the component.

While the object property names (firstName, lastName) follow camelCase convention for the API, the state variables (firstname, lastname) use lowercase. This inconsistency could lead to confusion.

Consider updating the state variables to match:

- const [firstname, setFirstname] = useState<string>('')
- const [lastname, setLastname] = useState<string>('')
+ const [firstName, setFirstName] = useState<string>('')
+ const [lastName, setLastName] = useState<string>('')

And update the corresponding references in the JSX.

app/client/components/profile/ClientProfile.tsx (1)

8-16: Improve image handling and accessibility

The profile image implementation needs improvements:

  1. Add loading="eager" for above-the-fold image
  2. Add proper alt text describing the user
  3. Consider using next/image's fill property for better responsive behavior
 <div className="relative flex items-center justify-center max-w-28 max-h-28 w-28 h-28 overflow-hidden rounded-full">
   <Image
-    className=""
-    src="/images/profile.jpg"
-    alt="User profile image"
-    width={500}
-    height={500}
+    className="object-cover"
+    src={user.profileImage || "/images/profile.jpg"}
+    alt={`Profile picture of ${user.firstName} ${user.lastName}`}
+    fill
+    loading="eager"
+    sizes="(max-width: 112px) 100vw, 112px"
   />
 </div>
app/chef/components/profile/ChefProfile.tsx (3)

10-16: Improve image handling and accessibility

The image component could be enhanced with better error handling and accessibility features.

 <Image
-  className=""
+  className="object-cover"
   src="/images/chef.png"
   alt="Chef profile image"
   width={500}
   height={500}
+  onError={(e) => {
+    e.currentTarget.src = '/images/default-chef.png'
+  }}
+  priority
 />

153-157: Fix typo and improve divider semantics

There's a typo in the className and the divider could be more semantic.

-<div className="flex justify center pt-8">
+<div className="flex justify-center pt-8">
-  <div className="border-l border-[#808080]"></div>
+  <hr className="h-full border-l border-[#808080]" role="separator" />
 </div>

171-176: Improve bio textarea handling

The bio textarea could be improved with better content handling.

 <textarea
   readOnly
-  className="w-full flex-grow px-4 py-2 rounded-lg bg-[#F2F4F8] border-b-2 border-[#C1C7CD] outline-none"
+  className="w-full min-h-[100px] px-4 py-2 rounded-lg bg-[#F2F4F8] border-b-2 border-[#C1C7CD] outline-none resize-none"
-  placeholder="Chef Tong is a seasoned culinary expert specializing in the art of steak-making. With his unique techniques and high-quality ingredients, he brings out the best flavors in every cut. Join him to discover the secrets behind achieving a perfectly cooked"
+  value={chef.bio}
 />
app/client/data/full-mock-data.ts (3)

Line range hint 9-989: Consider data validation for price consistency

There are some price inconsistencies in the data:

  • Similar courses have widely different prices (e.g., Thai courses range from 300-370)
  • Ingredient prices don't always correlate with course complexity (e.g., simple dishes sometimes have higher ingredient prices than complex ones)

Consider implementing a validation layer to ensure price consistency:

interface PriceRange {
  min: number;
  max: number;
}

const PRICE_RANGES: Record<string, PriceRange> = {
  'French': { min: 570, max: 680 },
  'Japanese': { min: 490, max: 600 },
  'Thai': { min: 300, max: 370 },
  // ... other categories
};

function validateCourseData(course: Course): boolean {
  const range = PRICE_RANGES[course.courseCategory];
  return course.coursePrice >= range.min && 
         course.coursePrice <= range.max &&
         course.ingredientPrice <= course.coursePrice * 0.15; // 15% rule
}

Line range hint 1-1005: Implement data normalization for chef ratings

Some chefs appear multiple times with inconsistent ratings:

  • Chef Sally has courses with different ratings
  • Chef Lorenzo appears in multiple courses with varying ratings

Consider normalizing chef data to maintain consistency:

interface ChefProfile {
  name: string;
  averageRating: number;
  specialties: string[];
  image: string;
}

const chefProfiles: Record<string, ChefProfile> = {
  'Chef Sally': {
    name: 'Chef Sally',
    averageRating: 4.0,
    specialties: ['American'],
    image: '/images/chef.png'
  },
  // ... other chefs
};

Line range hint 1-1005: Optimize image asset references

The mock data uses a limited set of images repeatedly. Consider optimizing this:

  • Most courses use 'ramachandra.png'
  • Chef images all use the same 'chef.png'

Consider implementing an image management system:

  1. Create category-specific default images
  2. Implement a CDN for image delivery
  3. Use image variants for different screen sizes
  4. Consider lazy loading for better performance

Would you like me to create a GitHub issue to track this optimization task?

app/chef/components/sign-up-chef/SignUpChef.tsx (9)

58-62: Provide user feedback on failed sign-up attempts

Currently, on a failed response, an error is logged to the console. Enhancing user feedback by displaying an error message on the UI can improve user experience.

Consider adding an error state and displaying a message to the user.


64-65: Enhance error handling in the catch block

The catch block logs the error to the console. Providing more detailed error information or displaying a user-friendly message can aid in debugging and improve user experience.


121-126: Add client-side email validation

To enhance user experience, consider adding client-side validation to check for a valid email format before form submission. This provides immediate feedback and can prevent unnecessary API calls.


135-140: Consider implementing password strength validation

Enhancing security by adding client-side password strength validation helps ensure users create strong passwords.

You might integrate a password strength meter or enforce minimum complexity requirements.


176-180: Ensure chefBio textarea can accommodate sufficient input

The textarea for chefBio should allow users to input their bio comfortably. Consider setting a suitable maxLength and handling overflow appropriately.


191-196: Validate and format phone number input

For the chefPhone field, consider using an input of type tel and adding client-side validation to ensure the phone number adheres to expected formats.

Suggest changing the input type:

<input
  name="chefPhone"
- type="text"
+ type="tel"
  placeholder="Phone"
  required
  value={formData.chefPhone}
  onChange={handleChange}
  className="..."
/>

204-209: Use a dropdown for chefSpecialty if options are predefined

If chefSpecialty should be selected from a set of predefined options, using a select element improves data consistency and user experience.

Example implementation:

- <input
-   name="chefSpecialty"
-   type="text"
-   placeholder="Chef Specialty"
-   required
-   value={formData.chefSpecialty}
-   onChange={handleChange}
-   className="..."
- />
+ <select
+   name="chefSpecialty"
+   required
+   value={formData.chefSpecialty}
+   onChange={handleChange}
+   className="..."
+ >
+   <option value="" disabled>Select your specialty</option>
+   <option value="Italian">Italian</option>
+   <option value="French">French</option>
+   <option value="Japanese">Japanese</option>
+   <!-- Add more options as needed -->
+ </select>

223-223: Provide descriptive alt text for uploaded image

To improve accessibility, update the alt attribute of the Image component to describe the image content meaningfully.

Suggest updating the alt text:

<Image
  src={coverImageUrl}
- alt="Cover"
+ alt="Uploaded chef profile picture"
  width={300}
  height={300}
  className="..."
  unoptimized
/>

223-223: Ensure image upload handles non-image files appropriately

While the upload widget is intended for images, users might attempt to upload non-image files. Implement validation to handle such cases gracefully.

Consider checking the file type before setting coverImageUrl.

app/client/components/group/payment/GroupPayment.tsx (3)

103-104: Add TypeScript type annotations to onPaid function

The function onPaid lacks type annotations for its parameter checkout_url. Adding type annotations enhances type safety and code clarity.

Apply the following change:

- const onPaid = (checkout_url) => {
+ const onPaid = (checkout_url: string): void => {

127-138: Improve error handling to provide user feedback

Currently, when the payment fails, the error is logged to the console, but the user is not informed. Consider displaying an error message to inform the user about the failure.

Apply the following change:

    } catch (error) {
      console.error('Error:', error)
+     // Display error message to the user
+     alert('Payment failed. Please try again.')
    }

132-132: Remove console.log statements from production code

It's best practice to avoid using console.log statements in production code. Consider removing this statement or replacing it with a proper logging mechanism.

Apply the following change:

-     console.log(responseData);
app/client/components/video/payment/VideoPayment.tsx (3)

80-84: Improve error handling by replacing 'alert' with form error message

Using alert disrupts user experience. Consider setting an error message that can be displayed inline within the form for a smoother user interaction.

Apply this diff to improve error handling:

80      if (value.startDate < today) {
-81        alert('Invalid date')
+81        setError('deliveryDate', {
+82          type: 'manual',
+83          message: 'Invalid date. Please select a future date.',
+84        })
85        return // Prevent further execution if the date is invalid
86      }

Also, ensure you import setError from react-hook-form:

106    const {
107      register,
108      handleSubmit,
109      setValue,
+110      setError,
111      formState: { errors },

29-42: Simplify Zod schema by adjusting 'shippingAddress' validation

Since shippingAddress is required when isDeliver is true, you can use .optional() with .refine() or use .string() conditionally based on isDeliver.

Consider modifying the schema as follows:

29    shippingAddress: isDeliver
-30      ? z.string()
-31      : z.string().optional(),
+30      ? z.string().nonempty('Shipping address is required when delivery is selected')
+31      : z.string().optional(),

Alternatively, you can keep it as is but ensure clarity in validation logic.


218-228: Improve accessibility by using a checkbox for the delivery option

Using a <button> to toggle a boolean state may confuse screen readers and impact accessibility. Consider using a checkbox input for better semantics.

Apply this diff to enhance accessibility:

218     <div className="flex items-center gap-x-2">
-219       <button
-220         type="button"
-221         className="flex items-center justify-center bg-[#697077] rounded-full w-5 h-5"
-222         onClick={toggleDelivery}
-223       >
-224         {isDeliver && (
-225           <div className="bg-[#231F1F] rounded-full border-2 w-4 h-4"></div>
-226         )}
-227       </button>
+219       <input
+220         type="checkbox"
+221         className="h-5 w-5 text-[#FE3511]"
+222         checked={isDeliver}
+223         onChange={toggleDelivery}
+224       />
228       <p className="font-semibold">Package with ingredients</p>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4163eb1 and 4fbda0f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (30)
  • .gitignore (1 hunks)
  • app/chef/components/Footer.tsx (1 hunks)
  • app/chef/components/Navbar.tsx (0 hunks)
  • app/chef/components/login&forget-password/Login.tsx (2 hunks)
  • app/chef/components/profile/ChefProfile.tsx (1 hunks)
  • app/chef/components/profile/EditChefProfile.tsx (1 hunks)
  • app/chef/components/sign-up-chef/SignUpChef.tsx (7 hunks)
  • app/chef/profile/edit-profile/page.tsx (1 hunks)
  • app/chef/profile/page.tsx (1 hunks)
  • app/client/components/group/payment/GroupPayment.tsx (1 hunks)
  • app/client/components/login&forget-password/Login.tsx (1 hunks)
  • app/client/components/profile/ClientProfile.tsx (1 hunks)
  • app/client/components/profile/CompleteCourse.tsx (1 hunks)
  • app/client/components/profile/CompleteCourseCard.tsx (1 hunks)
  • app/client/components/profile/EditClientProfile.tsx (1 hunks)
  • app/client/components/sign-up/SignUp.tsx (1 hunks)
  • app/client/components/video/course-detail/CourseDetailFirstPage.tsx (2 hunks)
  • app/client/components/video/payment/VideoCancelPage.tsx (1 hunks)
  • app/client/components/video/payment/VideoPayment.tsx (1 hunks)
  • app/client/components/video/payment/VideoThankYouPage.tsx (1 hunks)
  • app/client/data/full-mock-data.ts (84 hunks)
  • app/client/profile/edit-profile/page.tsx (1 hunks)
  • app/client/profile/page.tsx (1 hunks)
  • app/client/types/courses.d.ts (1 hunks)
  • app/client/video/complete/page.tsx (1 hunks)
  • app/client/video/incomplete/page.tsx (1 hunks)
  • app/client/video/payment/[courseId]/page.tsx (1 hunks)
  • app/unsigned/components/group/UpcomingWorkshopCard.tsx (3 hunks)
  • app/unsigned/types/group.d.ts (1 hunks)
  • package.json (1 hunks)
💤 Files with no reviewable changes (1)
  • app/chef/components/Navbar.tsx
✅ Files skipped from review due to trivial changes (2)
  • app/chef/profile/edit-profile/page.tsx
  • app/client/video/complete/page.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/client/components/profile/CompleteCourse.tsx
  • app/client/components/profile/CompleteCourseCard.tsx
  • app/client/profile/edit-profile/page.tsx
🧰 Additional context used
📓 Learnings (2)
app/client/video/payment/[courseId]/page.tsx (1)
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/group/payment/[groupId]/page.tsx:4-11
Timestamp: 2024-11-17T16:25:46.472Z
Learning: In `app/client/group/payment/[groupId]/page.tsx`, it is acceptable to omit TypeScript types and not utilize the `groupId` route parameter in the `GroupPaymentPage` component.
app/unsigned/types/group.d.ts (1)
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#21
File: app/client/types/group.d.ts:0-0
Timestamp: 2024-11-26T07:45:38.638Z
Learning: In `app/client/types/group.d.ts`, the `ingredientPrice` property in the `Group` interface is required and should not be optional.
🪛 eslint
app/chef/components/login&forget-password/Login.tsx

[error] 73-73: 'chefLoginData' is assigned a value but never used.

(@typescript-eslint/no-unused-vars)

🔇 Additional comments (22)
app/client/video/incomplete/page.tsx (1)

1-6: Verify PR scope and implementation

This file appears to be implementing video payment cancellation functionality, which differs from the PR objectives of implementing client/chef profile pages. Please clarify if:

  1. This file is intentionally part of this PR
  2. There are missing files containing the profile page implementations
  3. The PR description needs to be updated
✅ Verification successful

Let me generate additional scripts to verify the PR scope by checking the git changes.


The video cancellation page is correctly part of this PR

Based on the git diff and codebase analysis:

  1. The PR is implementing both profile pages (ChefProfile and ClientProfile) and includes the video cancellation functionality
  2. The changes are consistent with both profile implementations and payment-related features
  3. No issues found with the implementation scope
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for profile-related implementations that should be part of this PR
echo "Searching for profile-related files..."
fd -e tsx -e ts -e js -e jsx profile
fd -e tsx -e ts -e js -e jsx chef
fd -e tsx -e ts -e js -e jsx client

echo "Searching for profile-related components..."
rg -l "profile|Profile|chef|Chef" --type ts --type tsx

Length of output: 1231


Script:

#!/bin/bash
# Check the files changed in this PR
git diff --name-status HEAD~1

# Check the content of the profile implementation files
echo "=== Content of ChefProfile.tsx ==="
cat app/chef/components/profile/ChefProfile.tsx

echo "=== Content of ClientProfile.tsx ==="
cat app/client/components/profile/ClientProfile.tsx

# Check if the video cancel page is referenced elsewhere
echo "=== References to VideoCancelPage ==="
rg "VideoCancelPage"

Length of output: 14734

app/client/types/courses.d.ts (1)

6-6: Verify the usage of ingredientPrice across the codebase

Let's check how this new property is being used in the codebase to ensure consistent implementation.

✅ Verification successful

Let me try another search strategy to verify the usage.


Let me try one more time with a different approach using just the basic search without file type restrictions.


The ingredientPrice property is consistently implemented across the codebase

The property is properly used in:

  • Course interface definition in app/client/types/courses.d.ts
  • Mock data in app/client/data/full-mock-data.ts with consistent number values
  • Payment components in both VideoPayment.tsx and GroupPayment.tsx where it's used as courseAddOn and groupAddOn respectively
  • Similar interfaces in group.d.ts and ingredient.d.ts showing consistent pattern across related types
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of ingredientPrice to verify implementation
echo "Searching for ingredientPrice usage..."
rg "ingredientPrice" --type ts --type tsx -C 2

# Check if there are any Course object creations that might be affected
echo "Checking Course object creations..."
ast-grep --pattern 'const $_ : Course = {
  $$$
}'

Length of output: 295


Script:

#!/bin/bash
# Search for usage of ingredientPrice with correct file type
echo "Searching for ingredientPrice usage..."
rg "ingredientPrice" -t typescript -C 2

# Check for Course type usage and initialization
echo "Checking Course type usage..."
rg "Course" -t typescript -C 2

Length of output: 296


Script:

#!/bin/bash
# Search for any usage of ingredientPrice
echo "Searching for ingredientPrice usage..."
rg "ingredientPrice" -C 2

# Look for files with .ts extension containing Course
echo "Finding TypeScript files with Course..."
fd -e ts | xargs rg "Course" -C 2

Length of output: 40531

app/client/video/payment/[courseId]/page.tsx (1)

1-11: Verify PR scope alignment

This file implements video payment functionality, but the PR objectives focus on implementing client/chef profile pages. Please verify if this file should be included in this PR.

app/unsigned/types/group.d.ts (1)

6-6: LGTM! The ingredientPrice property is correctly typed and positioned.

The addition of the required number type for ingredientPrice is consistent with the interface's conventions and aligns with the learning that this field should not be optional.

app/client/profile/page.tsx (1)

1-5: Add type imports for TypeScript components

Since this is a TypeScript file (.tsx), we should explicitly import the types for the components.

+import type { FC } from 'react'
 import Navbar from '../components/Navbar'
 import ClientProfile from '../components/profile/ClientProfile'
 import CompleteCourse from '../components/profile/CompleteCourse'
 import Footer from '../components/Footer'
-export default function ClientProfilePage() {
+const ClientProfilePage: FC = () => {
package.json (1)

13-13: ⚠️ Potential issue

Version mismatch between @next/env and next package

The added @next/env@^15.0.3 is from a different major version than the [email protected] package. This version mismatch could lead to compatibility issues since @next/env is designed to work with its corresponding Next.js version.

Additionally, @next/env is already included as a transitive dependency through Next.js and doesn't need to be installed separately.

Apply this diff to remove the redundant dependency:

-    "@next/env": "^15.0.3",
app/client/components/video/payment/VideoCancelPage.tsx (1)

1-5: LGTM! Component setup and imports are appropriate.

The component is correctly set up with necessary imports and client-side rendering directive.

app/client/components/video/payment/VideoThankYouPage.tsx (1)

1-4: LGTM! Imports and client directive are properly configured.

app/client/components/video/course-detail/CourseDetailFirstPage.tsx (1)

4-7: LGTM! Router setup follows Next.js best practices

The implementation correctly uses the useRouter hook from next/navigation for client-side navigation.

app/unsigned/components/group/UpcomingWorkshopCard.tsx (2)

63-64: LGTM! Clean and efficient calculations

The transform and width calculations are well-implemented, providing smooth transitions and proper card spacing.


84-84: LGTM! Simplified navigation logic

The condition for showing the next button has been correctly simplified while maintaining the same functionality.

app/client/components/sign-up/SignUp.tsx (3)

48-50: Enhance error handling with specific error messages.

The current error handling doesn't utilize the server's response message.


31-34: Improve form validation implementation.

The current validation implementation can be enhanced with proper email format checking and field-specific errors.


Line range hint 1-180: Overall implementation looks good with room for improvements.

The component is well-structured with proper TypeScript usage, good accessibility, and solid UI implementation. The suggested improvements around error handling, validation, and naming consistency will enhance the user experience and code maintainability.

app/chef/components/profile/EditChefProfile.tsx (1)

1-6: LGTM! Component setup and imports are properly configured.

The necessary imports and client-side directive are correctly implemented.

app/client/components/login&forget-password/Login.tsx (1)

75-77: ⚠️ Potential issue

Avoid logging sensitive information to the console

The console.log statements at lines 75-77 log sensitive information, including user credentials and environment variables. This can lead to security risks if the logs are exposed.

Apply this diff to remove the sensitive logging:

-        console.log(JSON.stringify(clientLoginData)) // Log the data for debugging
-        console.log(process.env)
-        console.log(process.env.NEXT_PUBLIC_BACKEND_URL)
⛔ Skipped due to learnings
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#10
File: app/client/components/login&forget-password/Login.tsx:38-44
Timestamp: 2024-11-12T12:21:03.016Z
Learning: In `Login.tsx`, the `console.log` of `clientAuthJSON` is intentional mock data for development purposes as the code is unfinished.
app/chef/components/sign-up-chef/SignUpChef.tsx (6)

19-29: Initialize formData state with appropriate default values

The formData state is well-structured, capturing all necessary input fields for the form. Initializing the fields with empty strings ensures controlled components and prevents uncontrolled input warnings.


31-36: Properly handle form input changes with handleChange function

The handleChange function correctly updates the state based on the name and value of the input elements. This ensures that the formData state stays in sync with user inputs.


41-45: Ensure all required fields are included and correctly named

Double-check that all the necessary fields from formData, along with chefImage, are included in chefSignUpData and match the backend API's expected field names. Inconsistent naming could cause data mapping issues on the server side.


95-100: Ensure name attributes match formData keys

The name attribute of the chefName input matches the corresponding key in formData, ensuring that handleChange updates the correct state field.


107-112: Maintain consistency in input field handling

The chefSurname input correctly uses the name, value, and onChange properties, consistent with other input fields. This ensures reliable state management.


223-223: ⚠️ Potential issue

Remove unnecessary code in handleUploadSuccess function

In the handleUploadSuccess function, the line url.includes('image') has no effect since its return value isn't used. This line can be safely removed to clean up the code.

Suggest removing the unused line:

const handleUploadSuccess = (result: unknown) => {
  const uploadResult = result as UploadResult
  const url = uploadResult.info.secure_url
- url.includes('image') 
  setCoverImageUrl(url)
}

Likely invalid or redundant comment.

Comment on lines +4 to +6
export default function GroupCompletePage() {
return <VideoCancelPage />
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Resolve naming inconsistency

The component name GroupCompletePage is misleading as it's placed in the incomplete directory and renders a VideoCancelPage. Consider renaming to better reflect its purpose:

-export default function GroupCompletePage() {
+export default function VideoCancelPage() {
   return <VideoCancelPage />
 }
📝 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
export default function GroupCompletePage() {
return <VideoCancelPage />
}
export default function VideoCancelPage() {
return <VideoCancelPage />
}

@@ -3,6 +3,7 @@ export interface Course {
courseTitle: string;
courseCategory: string;
coursePrice: number;
ingredientPrice: number;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider making ingredientPrice optional to avoid breaking changes

Adding a required property to the Course interface is a breaking change that will require updates to all existing code that creates or uses Course objects. Consider making it optional with ? modifier initially:

-  ingredientPrice: number;
+  ingredientPrice?: number;
📝 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
ingredientPrice: number;
ingredientPrice?: number;

import Navbar from '../../../components/Navbar'
import VideoPayment from '../../../components/video/payment/VideoPayment'

export default function GroupPaymentPage() {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Rename component to match its purpose and location

The component name GroupPaymentPage is inconsistent with its location in the video payment route. This could lead to confusion in maintenance.

Apply this diff to fix the naming:

-export default function GroupPaymentPage() {
+export default function VideoPaymentPage() {
📝 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
export default function GroupPaymentPage() {
export default function VideoPaymentPage() {

Comment on lines +5 to +13
export default function ChefProfilePage() {
return (
<>
<Navbar />
<ChefProfile />
<Footer />
</>
)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add essential production-ready features

The component needs several improvements for production readiness:

  1. Error boundaries for graceful error handling
  2. Loading states for better user experience
  3. Metadata for SEO optimization
  4. TypeScript types for better type safety

Here's a suggested implementation:

import { Metadata } from 'next'
import { ErrorBoundary } from '@/components/ErrorBoundary'
import { LoadingSpinner } from '@/components/LoadingSpinner'

export const metadata: Metadata = {
  title: 'Chef Profile | WeCooked',
  description: 'View and manage your chef profile on WeCooked'
}

export default async function ChefProfilePage(): Promise<JSX.Element> {
  return (
    <ErrorBoundary fallback={<div>Something went wrong</div>}>
      <Navbar />
      <Suspense fallback={<LoadingSpinner />}>
        <ChefProfile />
      </Suspense>
      <Footer />
    </ErrorBoundary>
  )
}

💡 Codebase verification

Route protection needs to be implemented for chef profile

The verification reveals several issues that need to be addressed:

  • While authentication state management exists through AuthProvider and isAuthenticated state, there's no middleware or route protection mechanism to prevent unauthorized access to the chef profile page
  • The current implementation only uses client-side authentication checks in the Navbar component, which isn't secure
  • Navigation to the chef profile exists, but without proper route guards

Required actions:

  • Implement server-side middleware for route protection
  • Add proper authentication checks before rendering the chef profile page
  • Consider using Next.js middleware for protecting the entire /chef/* route group
🔗 Analysis chain

Verify route protection and navigation setup

Ensure that:

  1. The chef profile route is properly protected for authenticated users only
  2. Navigation to this page is correctly set up in the application

Let me check the implementation details of route protection and navigation setup.


Let me check for route protection implementation at the app level and middleware configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for route protection implementation
rg -l "middleware|auth.*protected|isAuthenticated" app/

# Check for navigation links to the chef profile
rg -l "href=.*chef/profile" app/

Length of output: 389


Script:

#!/bin/bash
# Check middleware implementation
rg -A 5 "middleware" app/

# Check navigation implementation in Navbar components
ast-grep --pattern 'href = "/chef/profile"' 

# Check for any auth protection in the chef profile route
rg -A 5 "protected|isAuthenticated" app/chef/

Length of output: 2634


Script:

#!/bin/bash
# Check for middleware.ts or similar files
fd middleware

# Check layout files for auth protection
fd "layout.tsx" app/chef/

# Check for auth protection in root layout
cat app/layout.tsx 2>/dev/null || true

Length of output: 899

Comment on lines +5 to +14
export default function ClientProfilePage() {
return (
<>
<Navbar />
<ClientProfile />
<CompleteCourse />
<Footer />
</>
)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding error boundaries and loading states

The component should handle potential errors from child components and show appropriate loading states while data is being fetched.

Consider implementing error boundaries and loading states:

+import { Suspense, ErrorBoundary } from 'react'
+
 export default function ClientProfilePage() {
   return (
     <>
       <Navbar />
-      <ClientProfile />
-      <CompleteCourse />
+      <ErrorBoundary fallback={<div>Something went wrong</div>}>
+        <Suspense fallback={<div>Loading profile...</div>}>
+          <ClientProfile />
+        </Suspense>
+        <Suspense fallback={<div>Loading courses...</div>}>
+          <CompleteCourse />
+        </Suspense>
+      </ErrorBoundary>
       <Footer />
     </>
   )
 }

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

Comment on lines 114 to 124
const token = localStorage.getItem('token')
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/payment/createPaymentForWorkshop`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'authorization': token, // Replace with actual token
},
body: JSON.stringify(clientPaymentData),
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle missing authentication token and correct the Authorization header

When retrieving the token from localStorage, it may be null if the user is not authenticated. Ensure you handle this case to prevent errors. Additionally, the authorization header should use a capital 'A' and include the authentication scheme, such as Bearer.

Apply the following changes:

  const token = localStorage.getItem('token')
+ if (!token) {
+   console.error('User is not authenticated')
+   // Handle unauthenticated state, e.g., redirect to login
+   return
+ }
  const response = await fetch(
    `${process.env.NEXT_PUBLIC_BACKEND_URL}/payment/createPaymentForWorkshop`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
-       'authorization': token, // Replace with actual token
+       'Authorization': `Bearer ${token}`,
      },
      body: JSON.stringify(clientPaymentData),
    }
  )
📝 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 token = localStorage.getItem('token')
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/payment/createPaymentForWorkshop`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'authorization': token, // Replace with actual token
},
body: JSON.stringify(clientPaymentData),
}
const token = localStorage.getItem('token')
if (!token) {
console.error('User is not authenticated')
// Handle unauthenticated state, e.g., redirect to login
return
}
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/payment/createPaymentForWorkshop`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(clientPaymentData),
}

Comment on lines 132 to 139
const token = localStorage.getItem('token')
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/payment/createPaymentForCourse`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
authorization: token, // Replace with actual token
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle missing authentication token and include 'Bearer' prefix in the authorization header

The token retrieved from localStorage might be null or undefined, leading to unauthorized requests. Additionally, the authorization header should include the Bearer prefix followed by the token.

Apply this diff to fix the issue:

132    const token = localStorage.getItem('token')
+133    if (!token) {
+134      console.error('User is not authenticated.')
+135      router.push('/login')
+136      return
+137    }
138      'Content-Type': 'application/json',
-139     authorization: token, // Replace with actual token
+139     'Authorization': `Bearer ${token}`,
📝 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 token = localStorage.getItem('token')
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/payment/createPaymentForCourse`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
authorization: token, // Replace with actual token
const token = localStorage.getItem('token')
if (!token) {
console.error('User is not authenticated.')
router.push('/login')
return
}
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/payment/createPaymentForCourse`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,

Comment on lines 99 to 101
const coursePrice = courses[ID].coursePrice
const courseAddOn = courses[ID].ingredientPrice
const coursePicture = courses[ID].courseImage
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure 'coursePrice' and 'courseAddOn' are numbers to avoid incorrect price calculations

If coursePrice and courseAddOn are strings, adding them will result in string concatenation rather than numerical addition. This can cause incorrect total price computations displayed to the user.

Apply this diff to fix the issue:

99    const courseTitle = courses[ID].courseTitle
-100   const coursePrice = courses[ID].coursePrice
-101   const courseAddOn = courses[ID].ingredientPrice
+100   const coursePrice = Number(courses[ID].coursePrice)
+101   const courseAddOn = Number(courses[ID].ingredientPrice)

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

Comment on lines 97 to 102
const ID = parseInt(courseId)
const courseTitle = courses[ID].courseTitle
const coursePrice = courses[ID].coursePrice
const courseAddOn = courses[ID].ingredientPrice
const coursePicture = courses[ID].courseImage

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for 'courseId' to prevent runtime errors

Accessing courses[ID] without validating ID may cause runtime errors if ID is out of bounds or courseId is invalid. This can lead to unexpected crashes.

Apply this diff to add validation:

97    const ID = parseInt(courseId)
+98    if (isNaN(ID) || ID < 0 || ID >= courses.length) {
+99      console.error('Invalid course ID')
+100     router.push('/error') // Or handle the error appropriately
+101     return
+102   }
103    const courseTitle = courses[ID].courseTitle

Comment on lines 121 to 123
const onPaid = (checkout_url) => {
router.push(checkout_url)
}
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Property name mismatch detected in payment handling

There's an inconsistency between the parameter name in the onPaid function (checkout_url) and how it's accessed from the response (checkoutUrl). While both components use the same pattern, this mismatch could cause issues:

  • app/client/components/video/payment/VideoPayment.tsx: Parameter uses checkout_url but accesses responseData.checkoutUrl
  • app/client/components/group/payment/GroupPayment.tsx: Same inconsistency

Consider updating the parameter name to match the camelCase convention used in the response object:

const onPaid = (checkoutUrl) => {
  router.push(checkoutUrl)
}
🔗 Analysis chain

Verify the correctness of the 'checkoutUrl' property in the payment response

Ensure that the payment API response contains the checkoutUrl property. If the property name differs, it could lead to redirection failures.

Run the following script to verify the property in the codebase:

Also applies to: 149-152

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify occurrences of 'checkoutUrl' in the codebase.

# Test: Search for 'checkoutUrl' usage. Expect: Correct property name is used consistently.
rg 'checkoutUrl'

# If 'checkoutUrl' is incorrect, update it to the correct property name from the API documentation.

Length of output: 201


Script:

#!/bin/bash
# Let's check the API response handling and type definitions to verify the property name
rg -A 5 -B 5 'type.*Response' app/
rg -A 5 -B 5 'interface.*Response' app/

Length of output: 78


Script:

#!/bin/bash
# Let's check the API response handling in the payment components
rg -A 10 -B 10 'onPaid.*checkout' app/client/components/

Length of output: 6397

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

🧹 Outside diff range and nitpick comments (20)
app/chef/components/sign-up-chef/ThankYouPage.tsx (1)

29-29: Consider updating the button label for clarity.

The button currently says "Home" but navigates to "/chef/course". Consider updating the label to better reflect its destination, such as "Go to Courses" or "View Courses".

-            Home
+            View Courses
app/unsigned/components/video/search/TopRateCourse.tsx (2)

Line range hint 24-24: Fix component naming inconsistency

The component name NewCourseCard doesn't match the file name TopRateCourse.tsx or its purpose of displaying top-rated courses.

Apply this change to maintain consistency:

-export default function NewCourseCard() {
+export default function TopRateCourse() {

Line range hint 46-71: Improve carousel implementation robustness

The carousel implementation has several potential issues:

  1. The next button condition startIndex + 1 < maxIndex might hide the last set of cards
  2. No handling for cases where courses.length isn't divisible by cardsToShow
  3. Missing error boundary and loading state

Consider these improvements:

-{startIndex + 1 < maxIndex && (
+{startIndex < maxIndex && (
   <button
     onClick={nextSlide}
     className="absolute right-4 top-1/2 -translate-y-1/2 bg-white p-2 rounded-full shadow-lg z-10"
   >
     <ChevronRightIcon className="w-6 h-6" />
   </button>
 )}

Also, consider adding error boundaries and loading states:

if (!courses?.length) {
  return <div className="text-white px-12">No courses available</div>;
}
app/unsigned/components/video/search/NewCourseCard.tsx (2)

Line range hint 7-65: Add accessibility and mobile support to the carousel.

The carousel implementation needs improvements in the following areas:

  1. Keyboard navigation support
  2. ARIA attributes for screen readers
  3. Touch/swipe support for mobile devices
  4. Debounced window resize listener

Consider applying these improvements:

 const useResponsiveCards = () => {
   const [cardsToShow, setCardsToShow] = useState<number>(3);
   
   useEffect(() => {
+    const debounce = (fn: Function, ms = 300) => {
+      let timeoutId: ReturnType<typeof setTimeout>;
+      return function (this: any, ...args: any[]) {
+        clearTimeout(timeoutId);
+        timeoutId = setTimeout(() => fn.apply(this, args), ms);
+      };
+    };
+
     const updateCards = () => {
       if (window.innerWidth < 640) setCardsToShow(1);
       else if (window.innerWidth < 1024) setCardsToShow(2);
       else setCardsToShow(3);
     };
     
-    window.addEventListener('resize', updateCards);
+    const debouncedUpdate = debounce(updateCards);
+    window.addEventListener('resize', debouncedUpdate);
     updateCards();
-    return () => window.removeEventListener('resize', updateCards);
+    return () => window.removeEventListener('resize', debouncedUpdate);
   }, []);
   
   return cardsToShow;
 };

 export default function NewCourseCard() {
   // ... existing state code ...

+  const handleKeyDown = (e: React.KeyboardEvent) => {
+    if (e.key === 'ArrowLeft') prevSlide();
+    if (e.key === 'ArrowRight') nextSlide();
+  };

   return (
-    <div className="flex flex-col justify-start py-12">
+    <div 
+      className="flex flex-col justify-start py-12"
+      role="region"
+      aria-label="New courses carousel"
+      tabIndex={0}
+      onKeyDown={handleKeyDown}
+    >
       <h1 className="text-3xl font-bold mb-2 text-white px-12">
         New Courses on Wecooked
       </h1>
       <div className="relative flex">
         <div className="flex overflow-hidden py-6">
           <div 
             className="flex transition-transform duration-300 ease-in-out"
             style={{ transform: `translateX(-${startIndex * (100 / cardsToShow)}%)` }}
+            role="list"
           >
             {courses.map((course, index) => (
-              <div key={index} className="w-full pl-12">
+              <div 
+                key={index} 
+                className="w-full pl-12"
+                role="listitem"
+              >
                 <CourseCard {...course} />
               </div>
             ))}
           </div>
         </div>
         
         {startIndex > 0 && (
           <button
             onClick={prevSlide}
+            aria-label="Previous slide"
             className="absolute left-4 top-1/2 -translate-y-1/2 bg-white p-2 rounded-full shadow-lg z-10"
           >
             <ChevronLeftIcon className="w-6 h-6" />
           </button>
         )}
         
         {startIndex + 1 < maxIndex && (
           <button
             onClick={nextSlide}
+            aria-label="Next slide"
             className="absolute right-4 top-1/2 -translate-y-1/2 bg-white p-2 rounded-full shadow-lg z-10"
           >
             <ChevronRightIcon className="w-6 h-6" />
           </button>
         )}
       </div>
     </div>
   );
 };

For touch support, consider adding a touch-enabled carousel library like react-swipeable or implementing touch events:

import { useSwipeable } from 'react-swipeable';

// Inside component:
const handlers = useSwipeable({
  onSwipedLeft: nextSlide,
  onSwipedRight: prevSlide,
  preventDefaultTouchmoveEvent: true,
});

// Add {...handlers} to the carousel container

Line range hint 26-65: Consider performance optimizations.

While the current implementation works well for the current use case, here are some suggestions to improve performance and maintainability:

  1. Memoize callback functions
  2. Use unique identifiers instead of array indices as keys
  3. Prevent unnecessary re-renders

Consider these optimizations:

 export default function NewCourseCard() {
   const [startIndex, setStartIndex] = useState<number>(0);
   const cardsToShow = useResponsiveCards();
   const maxIndex = Math.ceil(courses.length / cardsToShow) - 1;

-  const nextSlide = () => {
+  const nextSlide = useCallback(() => {
     setStartIndex((prev) => Math.min(prev + 1, maxIndex));
-  };
+  }, [maxIndex]);

-  const prevSlide = () => {
+  const prevSlide = useCallback(() => {
     setStartIndex((prev) => Math.max(prev - 1, 0));
-  };
+  }, []);

   return (
     // ... rest of the component ...
             {courses.map((course, index) => (
               <div 
-                key={index}
+                key={course.id} // Assuming courses have unique IDs
                 className="w-full pl-12"
               >
-                <CourseCard {...course} />
+                <memo.CourseCard {...course} />
               </div>
             ))}
     // ... rest of the component ...
   );
 };
app/client/components/video-player/VideoControl.tsx (1)

Line range hint 149-152: Improve modal accessibility and user experience

The current modal implementation lacks:

  1. Keyboard navigation (Esc to close)
  2. Focus trap within modal
  3. Backdrop overlay
  4. ARIA attributes for screen readers

Consider using a modal library like @headlessui/react or implement these improvements:

   {showRatingPopup && (
+    <>
+      <div 
+        className="fixed inset-0 bg-black bg-opacity-50 transition-opacity"
+        onClick={toggleRatingPopup}
+      />
       <div 
+        role="dialog"
+        aria-modal="true"
+        aria-labelledby="modal-title"
         className="absolute bg-white text-black w-[45%] h-[45%] px-6 py-4 rounded-lg shadow-lg flex flex-col items-center justify-center z-10"
       >
         <div 
+          id="modal-title"
           className="font-semibold text-3xl bg-gradient-to-b from-[#F0725C] to-[#FE3511] inline-block text-transparent bg-clip-text"
         >
           Rate this video
         </div>
app/client/components/video/payment/VideoPayment.tsx (1)

22-52: Consider simplifying the validation schema

The current validation schema could be simplified by using Zod's built-in conditional validation.

 const paymentSchema = (isDeliver: boolean) =>
   z.object({
     deliveryDate: z
-      .date()
-      .nullable()
-      .refine(
-        (date) => {
-          if (isDeliver) {
-            return !!date
-          }
-          return true
-        },
-        {
-          message: 'Delivery date is required when delivery is selected',
-        }
-      ),
+      .date()
+      .nullable()
+      .superRefine((date, ctx) => {
+        if (isDeliver && !date) {
+          ctx.addIssue({
+            code: z.ZodIssueCode.custom,
+            message: 'Delivery date is required when delivery is selected',
+          })
+        }
+      }),
     shippingAddress: z
       .string()
-      .optional()
-      .refine(
-        (address) => {
-          if (isDeliver) {
-            return !!address
-          }
-          return true
-        },
-        {
-          message: 'Shipping address is required when delivery is selected',
-        }
-      ),
+      .superRefine((address, ctx) => {
+        if (isDeliver && !address) {
+          ctx.addIssue({
+            code: z.ZodIssueCode.custom,
+            message: 'Shipping address is required when delivery is selected',
+          })
+        }
+      }),
   })
app/client/components/my-learning/GroupCardEnrolled.tsx (1)

Line range hint 30-63: Consider adding ARIA labels for better accessibility

The Link component contains complex content including an image and multiple text elements. Consider adding aria-labels to improve accessibility.

-    <Link href={`/client/group/learning/${groupId}`}>
+    <Link 
+      href={`/client/group/learning/${groupId}`}
+      aria-label={`View details for workshop: ${groupTitle} scheduled for ${formattedDate} at ${formattedTime}`}
+    >
app/chef/components/sign-up-chef/SignUpChef.tsx (1)

Line range hint 229-252: Add image upload validation

The image upload lacks size and type validation, which could lead to security issues.

Add validation to the upload widget:

 <CldUploadWidget
   uploadPreset={process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET}
+  options={{
+    maxFiles: 1,
+    maxFileSize: 5000000, // 5MB
+    acceptedFiles: '.jpg,.jpeg,.png',
+    sources: ['local'],
+  }}
   onSuccess={handleUploadSuccess}
 >
app/client/components/MostPopularCourse.tsx (1)

Line range hint 1-70: Consider consolidating duplicate carousel components

This component shares nearly identical code with app/unsigned/components/MostPopularCourse.tsx and app/client/components/video/search/TopRateCourse.tsx. Consider creating a reusable CourseCarousel component to reduce code duplication.

Example refactor:

// components/common/CourseCarousel.tsx
interface CourseCarouselProps {
  title: string;
  courses: Course[];
}

export function CourseCarousel({ title, courses }: CourseCarouselProps) {
  // ... shared carousel logic ...
}

// MostPopularCourse.tsx
export default function MostPopularCourse() {
  return <CourseCarousel title="Most Popular Course" courses={courses} />;
}
app/client/components/video/search/TopRateCourse.tsx (1)

Line range hint 22-22: Fix component naming inconsistency

The component is named NewCourseCard but:

  1. The file is named TopRateCourse.tsx
  2. The displayed title is "Top Rated Courses"
  3. The component renders a carousel, not a single card

Rename the component to match its purpose:

-export default function NewCourseCard() {
+export default function TopRatedCourses() {
app/unsigned/components/video/course-detail/CourseDetailFirstPage.tsx (1)

Line range hint 24-33: Update hardcoded description to match dynamic course content

The course description is hardcoded to describe a carbonara recipe, which may not match the actual course content being displayed.

Consider adding a description field to your course data structure and using it here:

-        <p className="text-xl max-w-2xl pt-12">
-          Master the art of authentic Italian carbonara using just five
-          ingredients: spaghetti, eggs, Pecorino Romano, guanciale, and black
-          pepper. Learn key techniques to create a creamy, flavorful dish
-          without cream, perfecting the balance of rich sauce and crispy
-          guanciale.
-        </p>
+        <p className="text-xl max-w-2xl pt-12">
+          {course.description}
+        </p>
app/client/components/video/course-detail/CourseDetailFirstPage.tsx (1)

12-12: Remove debug console.log statement

Debug logging should be removed before production deployment.

-   console.log("ID is",ID)
app/client/data/full-mock-data.ts (2)

Line range hint 261-267: Handle empty dietary restrictions consistently

Several courses have empty dietary restriction arrays. Consider:

  1. Either setting a default dietary category
  2. Or adding a "No Restrictions" category
    This would improve filtering and search functionality for users with dietary preferences.
-    courseDietary: [],
+    courseDietary: ['No Restrictions'],

Also applies to: 545-551, 753-759


Add price range validation for ingredient prices

The analysis confirms that while ingredientPrice is defined as a number type in both Course and Group interfaces, there are no validation constraints implemented. The mock data shows prices ranging from $10 to $30, with potential outliers at the higher end. Specific improvements needed:

  • Add validation to ensure ingredient prices stay within $10-$25 range (based on the most common price points in the data)
  • Consider implementing price tiers (e.g., basic: $10-15, premium: $16-20, luxury: $21-25)
  • Document the pricing strategy in the codebase

Locations needing attention:

  • app/client/types/courses.d.ts: Add price range validation for Course interface
  • app/client/types/group.d.ts: Add price range validation for Group interface
  • app/client/data/full-mock-data.ts: Adjust outlier prices ($28-30) to fit within the recommended range
🔗 Analysis chain

Line range hint 9-1005: Consider adding validation for ingredient prices

The ingredient prices vary significantly across courses ($10-$29). Consider implementing the following improvements:

  1. Add validation to ensure prices fall within a reasonable range
  2. Document the pricing strategy/policy
  3. Consider grouping courses by price ranges for better user experience

The output shows a clear distribution of ingredient prices across the mock data files. Let me verify if there are any validation mechanisms in place.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Analyze ingredient price distribution
# Expected: Show price ranges and identify potential outliers

echo "Ingredient Price Distribution:"
rg "ingredientPrice: \d+" -o | sort -n | uniq -c | sort -n

Length of output: 1967


Script:

#!/bin/bash
# Check for any validation logic related to ingredient prices
rg -A 5 -B 5 "ingredientPrice" --type ts | grep -i "valid\|check\|constraint"

# Look for any type definitions or interfaces that might constrain ingredient prices
ast-grep --pattern 'interface $_ {
  $$$
  ingredientPrice: $_
  $$$
}'

Length of output: 2737

app/client/components/group/course-detail/GroupCourseDetailFirstPage.tsx (1)

10-10: Remove debugging console.log statement

The console.log statement is useful during development but should be removed in production code to avoid unnecessary console output.

Apply this diff to remove the debugging statement:

- console.log("ID:", groupId)
app/client/components/group/GroupLearning.tsx (1)

14-14: Retrieve groupDate dynamically instead of hardcoding

Currently, groupDate is hardcoded as "November 15, 2024". For better maintainability, consider retrieving the date from the group data or another appropriate source.

Apply this diff to retrieve the date dynamically:

- const groupDate = "November 15, 2024"
+ const groupDate = group[ID].groupDate

Ensure that groupDate is defined in your group data structure.

app/chef/components/course/CourseUpload.tsx (3)

Line range hint 144-156: Avoid using document.querySelector in React components

The function updateBookmarkTime uses document.querySelector('video'), which can lead to issues in React applications. It's better to use a ref to directly access the video element.

Refactor the code to use useRef:

+  const videoRef = useRef<HTMLVideoElement>(null)

   const updateBookmarkTime = (id: number) => {
-    const videoElement = document.querySelector('video')
+    const videoElement = videoRef.current
     if (!videoElement) return

     const currentTime = videoElement.currentTime
     const minutes = Math.floor(currentTime / 60)
     const seconds = Math.floor(currentTime % 60)
     const timeString = `${minutes}:${seconds.toString().padStart(2, '0')}`

     setBookmarks(
       bookmarks.map((bookmark) =>
         bookmark.id === id
           ? {
               ...bookmark,
               time: timeString,
               timeStop: convertTimeToSeconds(timeString),
             }
           : bookmark
       )
     )
   }

...

           {videoUrl && (
             <video
               className="w-full rounded-lg mt-7"
+              ref={videoRef}
               controls
               src={videoUrl}
             />
           )}

...

Line range hint 65-78: Improve accessibility for toggle buttons

Ensure that the password toggle buttons have appropriate accessibility attributes.

Add aria-label to the toggle buttons:

  <button
    type="button"
    onClick={togglePasswordVisibility}
+   aria-label={showPassword ? 'Hide password' : 'Show password'}
    className="absolute right-4 -bottom-1 -translate-y-1/2"
  >

Apply the same change to the confirm password toggle button.


Line range hint 267-269: Remove unused state variable isUploadComplete if redundant

If isUploadComplete is no longer necessary after refactoring, consider removing it to clean up your state management.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4fbda0f and 1227111.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (29)
  • app/chef/components/Navbar.tsx (1 hunks)
  • app/chef/components/course/CourseUpload.tsx (1 hunks)
  • app/chef/components/login&forget-password/Login.tsx (3 hunks)
  • app/chef/components/profile/ChefProfile.tsx (1 hunks)
  • app/chef/components/profile/EditChefProfile.tsx (1 hunks)
  • app/chef/components/sign-up-chef/SignUpChef.tsx (6 hunks)
  • app/chef/components/sign-up-chef/ThankYouPage.tsx (1 hunks)
  • app/client/components/MostPopularCourse.tsx (1 hunks)
  • app/client/components/Navbar.tsx (1 hunks)
  • app/client/components/group/GroupLearning.tsx (2 hunks)
  • app/client/components/group/course-detail/GroupCourseDetailFirstPage.tsx (3 hunks)
  • app/client/components/group/payment/GroupPayment.tsx (4 hunks)
  • app/client/components/login&forget-password/Login.tsx (2 hunks)
  • app/client/components/my-learning/GroupCardEnrolled.tsx (1 hunks)
  • app/client/components/my-learning/GroupPage.tsx (1 hunks)
  • app/client/components/my-learning/ProgressPage.tsx (1 hunks)
  • app/client/components/video-player/VideoControl.tsx (1 hunks)
  • app/client/components/video/course-detail/CourseDetailFirstPage.tsx (3 hunks)
  • app/client/components/video/payment/VideoPayment.tsx (1 hunks)
  • app/client/components/video/search/NewCourseCard.tsx (1 hunks)
  • app/client/components/video/search/TopRateCourse.tsx (1 hunks)
  • app/client/data/full-mock-data.ts (84 hunks)
  • app/client/group/learning/[groupId]/page.tsx (1 hunks)
  • app/client/group/learning/page.tsx (0 hunks)
  • app/unsigned/components/MostPopularCourse.tsx (1 hunks)
  • app/unsigned/components/video/course-detail/CourseDetailFirstPage.tsx (3 hunks)
  • app/unsigned/components/video/search/NewCourseCard.tsx (1 hunks)
  • app/unsigned/components/video/search/TopRateCourse.tsx (1 hunks)
  • package.json (2 hunks)
💤 Files with no reviewable changes (1)
  • app/client/group/learning/page.tsx
✅ Files skipped from review due to trivial changes (2)
  • app/client/components/my-learning/ProgressPage.tsx
  • app/client/components/Navbar.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/chef/components/Navbar.tsx
  • app/chef/components/profile/ChefProfile.tsx
  • app/client/components/group/payment/GroupPayment.tsx
  • package.json
🧰 Additional context used
📓 Learnings (5)
app/client/group/learning/[groupId]/page.tsx (1)
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/group/learning/page.tsx:7-7
Timestamp: 2024-11-17T16:05:40.060Z
Learning: In `app/client/group/learning/page.tsx`, the `FAQPage` component is correctly imported from `../../components/FAQPage`.
app/unsigned/components/video/search/NewCourseCard.tsx (1)
Learnt from: sikares
PR: FWB-Creators/WeCooked-Frontend#8
File: app/client/components/Card.tsx:30-34
Timestamp: 2024-11-12T12:21:03.016Z
Learning: In the `Card` component located at `app/client/components/Card.tsx`, the `courses` data is static and does not require loading or error state handling.
app/unsigned/components/video/course-detail/CourseDetailFirstPage.tsx (1)
Learnt from: sikares
PR: FWB-Creators/WeCooked-Frontend#11
File: app/client/video/course-detail/[course_id]/page.tsx:9-21
Timestamp: 2024-11-12T12:21:03.016Z
Learning: In Next.js 13 using the app directory, dynamic route parameters should be accessed using the `useParams` hook from 'next/navigation', not passed through component props.
app/client/components/group/GroupLearning.tsx (1)
Learnt from: tong788
PR: FWB-Creators/WeCooked-Frontend#17
File: app/client/components/group/GroupLearning.tsx:15-45
Timestamp: 2024-11-16T20:48:57.204Z
Learning: In the `GroupLearning` component (`app/client/components/group/GroupLearning.tsx`), the data is already being handled elsewhere, so using hardcoded values is acceptable in this context.
app/client/components/video/course-detail/CourseDetailFirstPage.tsx (1)
Learnt from: sikares
PR: FWB-Creators/WeCooked-Frontend#11
File: app/client/video/course-detail/[course_id]/page.tsx:9-21
Timestamp: 2024-11-12T12:21:03.016Z
Learning: In Next.js 13 using the app directory, dynamic route parameters should be accessed using the `useParams` hook from 'next/navigation', not passed through component props.
🔇 Additional comments (23)
app/chef/components/sign-up-chef/ThankYouPage.tsx (1)

Line range hint 27-31: Verify the chef course route exists.

The navigation change from "/" to "/chef/course" looks good, but let's ensure this route is properly configured.

✅ Verification successful

Let me verify the content of the page component to ensure it's a valid route.


The /chef/course route is properly configured and implemented

The route exists and contains a valid page component that renders a course table with navigation, confirming it's an appropriate destination for chefs after sign-up.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of the chef course route
# Look for page component or route handler for /chef/course
fd -e tsx -e ts "page|route" app/chef/course/

Length of output: 72


Script:

#!/bin/bash
# Check the content of the chef course page component
cat app/chef/course/page.tsx

Length of output: 269

app/unsigned/components/video/search/TopRateCourse.tsx (1)

4-4: Verify data access pattern between unsigned and client components

The component is located in the unsigned directory but imports data from the client directory. This could potentially expose client-specific data to unsigned users.

Let's verify the data access patterns:

✅ Verification successful

Data source change is appropriate and safe

The change from @/app/unsigned/data/top-course to @/app/client/data/full-mock-data is acceptable because:

  1. Both unsigned and client directories maintain identical data structures with proper type definitions
  2. The full-mock-data contains all courses including those in top-course, ensuring data consistency
  3. The data is mock/sample data used for development, not sensitive production data
  4. Both directories have parallel JSON files in the public API folder, indicating an intentional data sharing pattern
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other unsigned components accessing client data
rg -t tsx -t ts "from '@/app/client/data'" "app/unsigned/"

# Check if the mock data is properly segregated
ast-grep --pattern 'export const courses = $_'

Length of output: 141


Script:

#!/bin/bash
# Check for other unsigned components accessing client data (fixed file type)
rg "from '@/app/client/data'" app/unsigned/

# Check the structure of mock data files
fd "mock-data" --type f

# Check the content of the mock data file
cat app/client/data/full-mock-data.ts

# Check the original data source
fd "top-course" --type f

Length of output: 27815


Script:

#!/bin/bash
# Check the content of both unsigned and client mock data files to verify if they are identical
cat app/unsigned/data/full-mock-data.ts

# Check the content of both top-course files to understand the data structure change
cat app/unsigned/data/top-course.ts
cat app/client/data/top-course.ts

Length of output: 32652

app/client/components/video/payment/VideoPayment.tsx (2)

79-83: Add validation for courseId to prevent runtime errors

As mentioned in a previous review, accessing courses[ID] without validation may cause runtime errors if ID is invalid.


107-133: Handle missing authentication token in payment submission

As mentioned in a previous review, the payment submission needs to include authentication token handling.

app/client/components/my-learning/GroupPage.tsx (1)

9-9: Verify terminology consistency across the application

The change from "Group Courses" to "Workshops" might affect user understanding. Let's ensure this terminology is consistent across the application.

✅ Verification successful

Terminology change is consistent with application-wide updates

The change from "Group Courses" to "Workshops" aligns with the broader application terminology. The codebase shows:

  • Most user-facing components use "Workshop" terminology (e.g., navigation, titles, landing pages)
  • Backend/internal references maintain "group-course" in file paths and data structures
  • UI components consistently use "Workshop" in user-visible text
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for instances of both terms to ensure consistent terminology
echo "Searching for 'Group Courses' terminology..."
rg -i "group.?courses?" --type=tsx --type=ts

echo -e "\nSearching for 'Workshops' terminology..."
rg -i "workshops?" --type=tsx --type=ts

Length of output: 352


Script:

#!/bin/bash
# Search for instances of both terms to ensure consistent terminology
echo "Searching for 'Group Courses' terminology..."
rg -i "group.?courses?"

echo -e "\nSearching for 'Workshops' terminology..."
rg -i "workshops?"

Length of output: 9122

app/client/components/my-learning/GroupCardEnrolled.tsx (1)

30-30: Verify the new route handler exists

The URL structure has changed from /client/group-learning/${groupId} to /client/group/learning/${groupId}. Let's ensure the new route is properly configured.

app/client/components/login&forget-password/Login.tsx (1)

69-98: 🛠️ Refactor suggestion

Enhance error handling and user feedback

The current implementation only logs errors to the console. Users need visual feedback when login fails.

Consider adding error state and displaying it to users:

+ const [loginError, setLoginError] = useState<string>('')

const handleFormSubmit = async (data: LoginFormData) => {
  try {
    // ... existing code ...
  } catch (error) {
    console.error('Login failed:', error)
+   setLoginError('Invalid email or password. Please try again.')
  }
}

// In the JSX, above the form:
+ {loginError && (
+   <div className="text-red-500 text-sm mb-4">
+     {loginError}
+   </div>
+ )}

Likely invalid or redundant comment.

app/chef/components/login&forget-password/Login.tsx (1)

60-64: ⚠️ Potential issue

Implement proper session handling for "Remember me"

The else branch is empty, leaving session-only authentication unhandled.

Add proper session handling:

 if (remember) {
   Cookies.set('Authorization', 'true', { expires: 7 })
 } else {
-  
+  Cookies.set('Authorization', 'true') // Session cookie
 }

Likely invalid or redundant comment.

app/chef/components/sign-up-chef/SignUpChef.tsx (2)

42-45: ⚠️ Potential issue

Fix incorrect API request payload structure

Similar to the login component, the signup data is incorrectly structured as an array.

Modify the data structure:

- const chefSignUpData = [{
+ const chefSignUpData = {
    ...formData,
    chefImage: coverImageUrl,
- }]
+ }

163-168: 🛠️ Refactor suggestion

Improve experience input field type and validation

Using a text input for experience years can lead to invalid data entry.

Update the input type and add validation:

 <input
   name="chefExperience"
-  type="text"
+  type="number"
+  min="0"
+  max="99"
   placeholder="Experience in years"
   required
   value={formData.chefExperience}
   onChange={handleChange}
   className="w-full px-4 py-2 rounded-lg bg-[#F2F4F8] border-b-2 border-[#C1C7CD] outline-none"
 />

Likely invalid or redundant comment.

app/client/components/MostPopularCourse.tsx (1)

3-3: Verify data structure compatibility with new import source

Please ensure that the courses from full-mock-data maintain the same structure expected by the CourseCard component.

✅ Verification successful

The previous search revealed the Course interface, but we need to verify the actual data structure in both files and how it's used in the CourseCard component to ensure compatibility. This will help us confirm if the data structure matches between the old and new sources.


The previous output showed us the Course interface and the full-mock-data structure, but we need to verify the CourseCard component's usage of the data and compare it with the most-popular-course data structure to ensure compatibility. This will help us confirm if the data structure matches between the old and new sources.


Based on the gathered information, I can now provide a final response. The search results have shown:

  1. The Course interface definition and full-mock-data structure
  2. How CourseCard is used in components with spread operator ({...course})
  3. The data structure in full-mock-data matches the Course interface
  4. The CourseCard component is used consistently across different components

Data structure compatibility verified

The new data source (full-mock-data) follows the same Course interface and structure as expected by the CourseCard component. The spread operator usage in components confirms that the same properties are being used, and the full-mock-data export shows it maintains the required structure with all necessary fields (courseId, courseTitle, courseCategory, coursePrice, reviewRating, chefName, courseImage, chefImage, and courseDietary).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the structure of course data in both old and new locations
echo "Searching for course type definitions and usages..."
rg -A 10 "interface.*Course" 
rg -A 5 "courses.*=.*" "@/app/client/data/"

Length of output: 6633


Script:

#!/bin/bash
# Let's check the actual content of both files to compare the data structure
echo "Content of full-mock-data:"
fd "full-mock-data" --exec cat {}

echo -e "\nContent of most-popular-course:"
fd "most-popular-course" --exec cat {}

# Also check how CourseCard uses the data
echo -e "\nCourseCard component usage:"
fd "CourseCard.tsx" --exec cat {}

Length of output: 78985


Script:

#!/bin/bash
# Let's check the CourseCard component implementation and most-popular-course data
echo "Searching for CourseCard component:"
rg -A 10 "CourseCard.*=.*" 

echo -e "\nSearching for most-popular-course data:"
rg -A 5 "export.*courses" "app/client/data/most-popular-course"

Length of output: 4263

app/unsigned/components/MostPopularCourse.tsx (1)

3-3: Review component location and access control

This component in the 'unsigned' directory appears identical to its counterpart in 'client'. Consider:

  1. Using a single shared component with authentication-based visibility control
  2. Ensuring appropriate data filtering for unsigned users
app/client/components/video/search/NewCourseCard.tsx (1)

3-3: Verify data structure compatibility after changing import source

The change in data source from new-course to full-mock-data needs verification to ensure all required fields are present and maintain the same structure.

✅ Verification successful

Let me gather more information about the component's usage of course properties.


Let me check the CourseCard component to verify how the course data is used.


Data structure compatibility verified - no issues found

The change in import from 'new-course' to 'full-mock-data' is safe. The courses data from 'full-mock-data' matches exactly with the Course interface and CourseCard component requirements. All required fields (courseId, courseTitle, courseCategory, coursePrice, reviewRating, chefName, courseImage, chefImage) are present in the mock data with correct types.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Compare the structure of courses data between old and new sources
# Check for any structural differences that might affect the component

# Search for type definitions and interfaces related to courses
rg -A 10 "interface.*Course|type.*Course" 

# Search for usage of course properties in the component
ast-grep --pattern 'courses.map((course, $_) => {
  $$$
})'

Length of output: 6492


Script:

#!/bin/bash
# Check the actual implementation of NewCourseCard to see which course properties are used
cat app/client/components/video/search/NewCourseCard.tsx

# Check the data structure in both old and new data sources
rg -l "new-course" app/client/data/
cat app/client/data/full-mock-data.ts

Length of output: 29938


Script:

#!/bin/bash
# Check the CourseCard component implementation
cat app/client/components/CourseCard.tsx

Length of output: 1731

app/client/components/video/course-detail/CourseDetailFirstPage.tsx (2)

57-61: 🛠️ Refactor suggestion

Improve navigation handling with loading state

The current navigation implementation lacks loading state and error handling.

As mentioned in a previous review, consider implementing loading state and error handling for navigation:

+  const [isLoading, setIsLoading] = useState(false)
   // ... rest of the component
   <button
     className="px-8 py-2.5 bg-gradient-to-b from-[#F0725C] to-[#FE3511] text-white rounded-full font-semibold transition-all duration-300 transform hover:scale-105 hover:shadow-lg"
-    onClick={() => router.push(`/client/video/payment/${ID}`)}
+    onClick={async () => {
+      try {
+        setIsLoading(true)
+        await router.push(`/client/video/payment/${ID}`)
+      } catch (error) {
+        console.error('Navigation failed:', error)
+        // Add error notification here
+      } finally {
+        setIsLoading(false)
+      }
+    }}
+    disabled={isLoading}
   >
-    Let&apos;s Cook
+    {isLoading ? 'Loading...' : 'Let\'s Cook'}
   </button>

10-17: ⚠️ Potential issue

Add error handling for course data access

Similar to the unsigned version, this component needs proper error handling for course data access.

Apply the same error handling pattern as suggested for the unsigned version to prevent runtime errors from invalid course IDs or missing data.

app/client/data/full-mock-data.ts (1)

776-777: Fix inconsistent course titles

There are course titles containing the word "courseCategory" which appears to be a mistake.

Also applies to: 848-849

app/client/group/learning/[groupId]/page.tsx (1)

1-26: LGTM!

The GroupLearningPage component correctly sets up the layout, imports necessary components, and implements error handling with ErrorBoundary. The structure is clean and organized.

app/client/components/group/GroupLearning.tsx (2)

19-19: Make the layout responsive for different screen sizes

Using a fixed height of 750px might cause layout issues on smaller screens. Consider using responsive units or minimum height to improve adaptability.

Apply this diff to enhance responsiveness:

- <div className="h-[750px] relative w-full flex flex-col items-center">
+ <div className="min-h-screen relative w-full flex flex-col items-center py-20">

Line range hint 21-25: Enhance the alt text for better accessibility

The current alt text "Cooked Image" is not very descriptive. Providing a more detailed alt text improves accessibility for users relying on screen readers.

Apply this diff to improve the alt text:

- alt="Cooked Image"
+ alt={`Image of ${groupTitle}`}
app/chef/components/profile/EditChefProfile.tsx (4)

8-14: Reorder state declarations and implement form state management

It's good practice to declare state variables before using them in functions. Also, the component lacks form state management for inputs.

Reorder the state declarations and add form state:

 export default function EditChefProfile() {
+  const [formData, setFormData] = useState({
+    firstName: '',
+    lastName: '',
+    gender: '',
+    email: '',
+    phone: '',
+    password: '',
+    confirmPassword: '',
+    experience: '',
+    specialties: '',
+    bio: '',
+  })
+
+  const [showPassword, setShowPassword] = useState<boolean>(false)
+  const [showConfirmPassword, setShowConfirmPassword] = useState<boolean>(false)
+
+  const handleInputChange = (e) => {
+    const { name, value } = e.target
+    setFormData((prev) => ({ ...prev, [name]: value }))
+  }
+
   const togglePasswordVisibility = () => setShowPassword((prev) => !prev)
   const toggleConfirmPasswordVisibility = () =>
     setShowConfirmPassword((prev) => !prev)

Update input fields to use formData and handleInputChange:

 <input
   type="text"
+  name="firstName"
+  value={formData.firstName}
+  onChange={handleInputChange}
   className="w-full px-4 py-2..."
   placeholder="Arm"
 />

34-152: ⚠️ Potential issue

Add form submission handler and input validation

The form currently lacks a submission handler (onSubmit) and input validation. This is essential to process form data securely and provide feedback to the user.

Implement an onSubmit handler and input validation:

+ const [formData, setFormData] = useState({
+   firstName: '',
+   lastName: '',
+   gender: '',
+   email: '',
+   phone: '',
+   password: '',
+   confirmPassword: '',
+   experience: '',
+   specialties: '',
+   bio: '',
+ })
+
+ const [errors, setErrors] = useState({})
+
+ const handleInputChange = (e) => {
+   const { name, value } = e.target
+   setFormData((prev) => ({ ...prev, [name]: value }))
+   setIsDirty(true)
+ }
+
+ const handleSubmit = (e) => {
+   e.preventDefault()
+   // Validate input fields
+   // Submit form data to API
+ }
+
 <form
+  onSubmit={handleSubmit}
 >
   <div className="grid grid-cols-10 h-full mt-8">

   {/* Update input fields */}
     <input
       type="text"
+      name="firstName"
+      value={formData.firstName}
+      onChange={handleInputChange}
       className="w-full px-4 py-2..."
       placeholder="Arm"
     />
+    {errors.firstName && (
+      <p className="text-red-500 text-sm mt-1">{errors.firstName}</p>
+    )}

   {/* Repeat for other input fields */}

153-166: 🛠️ Refactor suggestion

Replace Links with form buttons and add unsaved changes protection

Using <Link> for 'Cancel' and 'Save' doesn't handle form submission or unsaved changes properly.

Replace <Link> components with <button> elements:

 <div className="mt-16 flex justify-end items-center gap-x-8">
-  <Link
-    href="/chef/profile"
+  <button
+    type="button"
+    onClick={handleCancel}
     className="text-transparent bg-clip-text..."
   >
     Cancel
-  </Link>
+  </button>
-  <Link
-    href="/chef/profile"
+  <button
+    type="submit"
     className="flex items-center justify-center..."
   >
     Save
-  </Link>
+  </button>
 </div>

Add unsaved changes protection:

const [isDirty, setIsDirty] = useState(false)

useEffect(() => {
  const handleBeforeUnload = (e: BeforeUnloadEvent) => {
    if (isDirty) {
      e.preventDefault()
      e.returnValue = ''
    }
  }

  window.addEventListener('beforeunload', handleBeforeUnload)
  return () => window.removeEventListener('beforeunload', handleBeforeUnload)
}, [isDirty])

const handleCancel = () => {
  if (isDirty) {
    const confirmLeave = window.confirm('You have unsaved changes. Are you sure you want to leave?')
    if (!confirmLeave) return
  }
  router.push('/chef/profile')
}

19-31: 🛠️ Refactor suggestion

Implement image upload functionality

The profile image section currently doesn't support uploading a new profile picture.

Enhance the image section to allow image uploads:

 <div className="relative flex items-center justify-center max-w-28 max-h-28 w-28 h-28 overflow-hidden rounded-full">
+  <input
+    type="file"
+    accept="image/*"
+    className="hidden"
+    id="profile-image"
+    onChange={handleImageUpload}
+  />
   <Image
     src="/images/profile.jpg"
     alt="Chef profile image"
     width={500}
     height={500}
   />
+  <div
+    className="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity cursor-pointer"
+    onClick={() => document.getElementById('profile-image')?.click()}
+  >
+    <PencilIcon className="text-white w-6 h-6" />
+  </div>
 </div>

Implement the handleImageUpload function to handle the selected image file.

@@ -1,6 +1,6 @@
"use client";
import { useState, useEffect } from "react";
import { courses } from "@/app/unsigned/data/new-course";
import { courses } from '@/app/client/data/full-mock-data'
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Component name misaligns with implementation

The component displays all courses from the mock data without any filtering for "new" courses, despite being named "NewCourseCard" and having a heading "New Courses on Wecooked". The courses data structure also doesn't contain any fields to identify new or recent courses. Consider either:

  • Adding a isNew or dateAdded field to the courses data to filter new courses
  • Renaming the component to better reflect its current functionality (e.g., CourseCarousel or FeaturedCourses)
🔗 Analysis chain

LGTM! Consider component naming alignment.

The consolidation of mock data into a single source (full-mock-data) improves maintainability. However, since this component is named "NewCourseCard", verify that the courses from the full mock data correctly represent new courses.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the structure and content of the mock data
cat app/client/data/full-mock-data.ts | grep -A 10 "export const courses"

Length of output: 389


Script:

#!/bin/bash
# Let's check if there's any "new" or "latest" related fields in the courses data
rg -i "new|latest" app/client/data/full-mock-data.ts

# Also check the component implementation to understand how it filters/uses the courses
cat app/unsigned/components/video/search/NewCourseCard.tsx

Length of output: 2523

if (response.ok) {
setShowRatingPopup(false)
} else {
console.error('Failed to sign up')
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect error message

The error message refers to "sign up" instead of review submission.

-        console.error('Failed to sign up')
+        console.error('Failed to submit review')
📝 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
console.error('Failed to sign up')
console.error('Failed to submit review')

Comment on lines +44 to +72
const handleSubmitReview = async() => {

const ratingCourseData = {
rating: rating,
courseId: videoID,
reviewDetail: comment,
}

console.log('Rating:', rating)
console.log('Comment:', comment)
setShowRatingPopup(false)

try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/user/ratingcourse`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(ratingCourseData),
}
)

if (response.ok) {
setShowRatingPopup(false)
} else {
console.error('Failed to sign up')
}
} catch (error) {
console.error('Error submitting the form:', error)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add authentication and improve error handling in review submission

Several improvements are needed in the review submission implementation:

  1. Missing authentication token
  2. No input validation
  3. Generic error handling
  4. Missing loading state

Apply this diff to fix the issues:

+  const [isSubmitting, setIsSubmitting] = useState(false)
+
   const handleSubmitReview = async() => {
+    if (rating === 0) {
+      console.error('Please select a rating')
+      return
+    }
+
+    const token = localStorage.getItem('token')
+    if (!token) {
+      console.error('User is not authenticated')
+      router.push('/login')
+      return
+    }
+
+    setIsSubmitting(true)
     const ratingCourseData = {
       rating: rating,
       courseId: videoID,
       reviewDetail: comment,
     }

     try {
       const response = await fetch(
         `${process.env.NEXT_PUBLIC_BACKEND_URL}/user/ratingcourse`,
         {
           method: 'POST',
           headers: { 
             'Content-Type': 'application/json',
+            'Authorization': `Bearer ${token}`
           },
           body: JSON.stringify(ratingCourseData),
         }
       )

       if (response.ok) {
         setShowRatingPopup(false)
+        // Show success message
+        alert('Review submitted successfully')
       } else {
-        console.error('Failed to sign up')
+        const errorData = await response.json()
+        console.error('Failed to submit review:', errorData)
+        alert('Failed to submit review. Please try again.')
       }
     } catch (error) {
       console.error('Error submitting the form:', error)
+      alert('An error occurred. Please try again.')
     } finally {
+      setIsSubmitting(false)
     }
   }

Also update the submit button to show loading state:

   <button
     onClick={handleSubmitReview}
+    disabled={isSubmitting}
     className="bg-gradient-to-b from-[#F0725C] to-[#FE3511] text-white px-4 py-2 rounded-md"
   >
-    Submit
+    {isSubmitting ? 'Submitting...' : 'Submit'}
   </button>

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

Comment on lines +91 to +93
const responseData = await response.json()
console.log(responseData)
Cookies.set('Authorization', responseData.token)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add token validation before storage

The token is stored without validation of its structure or content.

Add token validation before storage:

 const responseData = await response.json()
+ if (!responseData.token || typeof responseData.token !== 'string') {
+   throw new Error('Invalid token received')
+ }
 console.log(responseData)
 Cookies.set('Authorization', responseData.token)
📝 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 responseData = await response.json()
console.log(responseData)
Cookies.set('Authorization', responseData.token)
const responseData = await response.json()
if (!responseData.token || typeof responseData.token !== 'string') {
throw new Error('Invalid token received')
}
console.log(responseData)
Cookies.set('Authorization', responseData.token)

Comment on lines +74 to +77
const chefLoginData = [{
chefEmail: data.email,
chefPassword: data.password,
}]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect API request payload structure

The chefLoginData is incorrectly structured as an array with a single object, which may not match the API's expectations.

Modify the data structure:

- const chefLoginData = [{
+ const chefLoginData = {
    chefEmail: data.email,
    chefPassword: data.password,
- }]
+ }
📝 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 chefLoginData = [{
chefEmail: data.email,
chefPassword: data.password,
}]
const chefLoginData = {
chefEmail: data.email,
chefPassword: data.password,
}

Comment on lines +8 to +15
const { groupId } = useParams<{ groupId: string }>() // TypeScript typing for useParams
const ID = parseInt(groupId)
console.log("ID:",groupId)
const groupTitle = group[ID].groupTitle
const chefImage = group[ID].chefImage
const chefName = group[ID].chefName
const groupPicture = group[ID].groupPicture
const groupCategory = group[ID].groupCategory
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for invalid groupId

Currently, the code assumes that groupId can be parsed into a valid integer and that group[ID] exists. If groupId is invalid or does not correspond to an existing group, accessing group[ID] could lead to runtime errors.

Apply this diff to add error handling:

const params = useParams()
const groupId = params.groupId as string
const ID = parseInt(groupId)
+ if (isNaN(ID) || !group[ID]) {
+   // Handle invalid groupId, maybe redirect or show an error message
+   return <div>Group not found</div>
+ }
const groupTitle = group[ID].groupTitle
const chefImage = group[ID].chefImage
// ... rest of the code

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


export default function GroupLearning() {

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the TypeScript typing of useParams

The useParams hook from 'next/navigation' does not support generic typing. Using <{ groupId: string }> has no effect and may lead to TypeScript issues.

Apply this diff to correct the typing:

- const { groupId } = useParams<{ groupId: string }>() // TypeScript typing for useParams
+ const params = useParams()
+ const groupId = params.groupId as string
📝 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 params = useParams()
const groupId = params.groupId as string

Comment on lines +6 to +15

const { groupId } = useParams<{ groupId: string }>() // TypeScript typing for useParams
const ID = parseInt(groupId)
const groupTitle = group[ID].groupTitle
const chefImage = group[ID].chefImage
const chefName = group[ID].chefName
const groupPicture = group[ID].groupPicture
const groupDetail = group[ID].groupDetail
const groupDate = "November 15, 2024"

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for invalid groupId

The code assumes groupId is a valid integer and that group[ID] exists. If groupId is invalid or doesn't correspond to an existing group, it could cause runtime errors.

Apply this diff to add error handling:

const params = useParams()
const groupId = params.groupId as string
const ID = parseInt(groupId)
+ if (isNaN(ID) || !group[ID]) {
+   // Handle invalid groupId, maybe redirect or show an error message
+   return <div>Group not found</div>
+ }
const groupTitle = group[ID].groupTitle
const chefImage = group[ID].chefImage
// ... rest of the code

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

@@ -97,6 +97,46 @@ export default function CourseUpload() {
setIsSubmitting(true)
setSubmitError(null)

const courseUploadData = [
{
courseTitle: 'Masterbate British Cuisine',
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the typo in courseTitle

The courseTitle contains a typo: 'Masterbate British Cuisine' should be corrected to 'Master British Cuisine'.

Apply this diff to fix the typo:

-        courseTitle: 'Masterbate British Cuisine',
+        courseTitle: 'Master British Cuisine',
📝 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
courseTitle: 'Masterbate British Cuisine',
courseTitle: 'Master British Cuisine',

Comment on lines +100 to +138
const courseUploadData = [
{
courseTitle: 'Masterbate British Cuisine',
courseDetail:
'Learn the secrets of British cuisine from Gordon Ramsay.',
coursePrice: 100,
courseCategory: 'Cooking',
courseVideoId: 1,
courseChefId: 1,
coursePackId: 'pack1',
courseVideoPath: 'test12345678.com',
courseIngredientPrice: 123456,
courseIngredientDetail: 'Pork,Pork,Pork,Pork',
courseImage: 'placeholderImage.com',
},
]
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/chef/upload`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(courseUploadData),
}
)

if (response.ok) {
const responseData = await response.json()
console.log('Upload successful:', responseData)
} else {
const errorDetails = await response.json() // Optional: read error response
console.error('Failed to upload:', response.status, errorDetails)
}
} catch (error) {
console.error('Error submitting the form:', error)
}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor duplicate submission logic

There is duplicate submission logic in the handleSubmit function. Currently, you are sending courseUploadData to the backend and then calling mockSubmitToAPI with courseData. This can lead to inconsistent data submissions and unnecessary complexity.

Consider refactoring the code to use the form data (courseData) for the backend submission and remove the redundant courseUploadData. Here's how you can modify your code:

-    const courseUploadData = [
-      {
-        courseTitle: 'Master British Cuisine',
-        courseDetail:
-          'Learn the secrets of British cuisine from Gordon Ramsay.',
-        coursePrice: 100,
-        courseCategory: 'Cooking',
-        courseVideoId: 1,
-        courseChefId: 1,
-        coursePackId: 'pack1',
-        courseVideoPath: 'test12345678.com',
-        courseIngredientPrice: 123456,
-        courseIngredientDetail: 'Pork,Pork,Pork,Pork',
-        courseImage: 'placeholderImage.com',
-      },
-    ]
     try {
+      const courseData: CourseSubmission = {
+        courseName,
+        courseDetail,
+        courseCategory,
+        courseDietary,
+        coursePrice,
+        coursePackPrice,
+        courseIngredients,
+        videoUrl,
+        coverImageUrl,
+        bookmarks: bookmarks.map((b) => ({
+          title: b.title,
+          time: b.time,
+          timeStop: b.timeStop,
+          timeCountdown: b.timeCountdown,
+        })),
+      }
+
       const response = await fetch(
         `${process.env.NEXT_PUBLIC_BACKEND_URL}/chef/upload`,
         {
           method: 'POST',
           headers: {
             'Content-Type': 'application/json',
           },
-          body: JSON.stringify(courseUploadData),
+          body: JSON.stringify(courseData),
         }
       )

       if (response.ok) {
         const responseData = await response.json()
         console.log('Upload successful:', responseData)
+        setSubmitSuccess(true)
+        resetForm()
       } else {
         const errorDetails = await response.json() // Optional: read error response
         console.error('Failed to upload:', response.status, errorDetails)
       }
     } catch (error) {
       console.error('Error submitting the form:', error)
+      setSubmitError(error instanceof Error ? error.message : 'Unknown error')
     } finally {
       setIsSubmitting(false)
     }

-    try {
-      const response = await mockSubmitToAPI(courseData)
-      if (response.success) {
-        setSubmitSuccess(true)
-        // Reset form or navigate away
-        resetForm()
-      } else {
-        throw new Error(response.message || 'Course submission failed')
-      }
-    } catch (error) {
-      setSubmitError(error instanceof Error ? error.message : 'Unknown error')
-    } finally {
-      setIsSubmitting(false)
-    }

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

@tong788 tong788 changed the title [WC-28, WC-162] - Implement Client/Chef profile page, Implement edit Client/Chef profile page [WC-28, WC-162] - Implement Client/Chef profile page, Implement edit Client/Chef profile page + API connection Dec 9, 2024
@coderabbitai coderabbitai bot mentioned this pull request Dec 9, 2024
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants