Skip to content

Revamp homepage featured courses and CTA#25

Open
djharga wants to merge 1 commit intomainfrom
codex/conduct-comprehensive-frontend-audit
Open

Revamp homepage featured courses and CTA#25
djharga wants to merge 1 commit intomainfrom
codex/conduct-comprehensive-frontend-audit

Conversation

@djharga
Copy link
Owner

@djharga djharga commented Nov 25, 2025

User description

User description

Summary

  • Refresh the featured courses slider with richer metadata, accessibility hooks, and reduced-motion support
  • Switch course imagery to Next.js Image components, add course stats grid, and expose a direct link to all courses
  • Modernize the CTA card with layered gradients, accessible links, and a secondary consulting action

Testing

  • npm run lint (fails: existing react-hooks/exhaustive-deps rule missing, forbidden require usage in tailwind.config.ts, and parsing error in temp_check.js)

Codex Task


PR Type

Enhancement


Description

  • Modernize CTA section with layered gradients, improved accessibility, and secondary consulting action

  • Enhance featured courses slider with Next.js Image optimization and reduced-motion support

  • Add rich course metadata display including stats grid, ratings, and direct link to all courses

  • Improve visual design with backdrop blur effects, better typography, and focus-visible states


Diagram Walkthrough

flowchart LR
  A["CTA Section"] -->|"Add gradients & accessibility"| B["Modernized CTA"]
  C["Featured Courses"] -->|"Next.js Image & stats"| D["Enhanced Slider"]
  B -->|"Secondary action"| E["Consulting Link"]
  D -->|"Reduced motion support"| F["Accessible Animations"]
  D -->|"Rich metadata"| G["Course Stats Grid"]
Loading

File Walkthrough

Relevant files
Enhancement
CTASection.tsx
Modernize CTA with gradients and accessibility                     

src/components/homepage/CTASection.tsx

  • Add layered radial gradients and backdrop blur for modern visual depth
  • Implement accessibility improvements with aria-labelledby,
    aria-hidden, and focus-visible outlines
  • Add secondary consulting action button alongside primary registration
    CTA
  • Enhance typography with improved spacing, leading, and gradient text
    effects
  • Add badge section highlighting certified programs and digital
    certificates
+28/-21 
FeaturedCoursesSection.tsx
Optimize slider with Next.js Image and accessibility         

src/components/homepage/FeaturedCoursesSection.tsx

  • Replace standard img tags with Next.js Image component for
    optimization and lazy loading
  • Integrate useReducedMotion hook to respect user motion preferences in
    animations
  • Add comprehensive course stats grid displaying lessons, duration, and
    student count
  • Implement course rating display with star icon and 5-star scale
  • Add direct link to browse all courses at bottom of slider
  • Enhance accessibility with ARIA labels, roles, and focus-visible
    states on interactive elements
  • Improve visual design with better spacing, typography, and color
    contrast
+111/-75


CodeAnt-AI Description

Refresh homepage CTA and featured courses highlights

What Changed

  • CTA card now sits on a layered gradient background, explains the offer in more depth, and exposes both the register action and a consulting link with visible focus indicators.
  • Featured courses header adds a descriptive badge, and the slider uses optimized imagery, clearer stat badges, and a dedicated link to browse all courses.
  • Slider auto-advance honors reduced-motion preferences, navigation dots/buttons show focus states, and each card now highlights duration, rating, price, and learner counts before you enter a course page.

Impact

✅ Clearer CTA choices
✅ Reduced motion friendly slider
✅ More upfront course details

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

ملاحظات الإصدار

  • ميزات جديدة

    • تحديث تصميم قسم العروض الترويجية مع عناصر بصرية محسّنة وتدرجات لونية.
    • إضافة محتوى دورات أغنى يتضمن الصور والمعلومات التفصيلية والتقييمات والأسعار.
    • عرض إحصائيات الدورات بصيغة شبكة تفاعلية.
  • تحسينات التصميم

    • تحسين مظهر البطاقات بزوايا منحنية وتأثيرات بصرية حديثة.
    • تعزيز الوصول للجميع وتجربة لوحة المفاتيح.
    • احترام تفضيلات الحركة المنخفضة للمستخدمين.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Nov 25, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
khatwa-learning-platform Ready Ready Preview Comment Nov 25, 2025 2:42am

@codeant-ai
Copy link

codeant-ai bot commented Nov 25, 2025

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@netlify
Copy link

netlify bot commented Nov 25, 2025

Deploy Preview for scintillating-centaur-5d4290 ready!

Name Link
🔨 Latest commit edef020
🔍 Latest deploy log https://app.netlify.com/projects/scintillating-centaur-5d4290/deploys/69251736a1c48c00080a28c5
😎 Deploy Preview https://deploy-preview-25--scintillating-centaur-5d4290.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 25, 2025

Deploy Preview for khatw ready!

Name Link
🔨 Latest commit edef020
🔍 Latest deploy log https://app.netlify.com/projects/khatw/deploys/69251736ea39b500073b9527
😎 Deploy Preview https://deploy-preview-25--khatw.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@sourcery-ai
Copy link

sourcery-ai bot commented Nov 25, 2025

Reviewer's Guide

Revamps the homepage featured courses slider and call-to-action section to use richer course metadata, Next.js Image, reduced-motion-aware animations, and more accessible, modern CTAs including a secondary consulting path.

Sequence diagram for homepage featured courses slider with reduced motion and autoplay

sequenceDiagram
  actor User
  participant Browser
  participant FeaturedCoursesSection
  participant useReducedMotionHook
  participant FramerMotion
  participant NextRouter
  participant CoursesPage

  User->>Browser: Open homepage
  Browser->>FeaturedCoursesSection: Render component
  FeaturedCoursesSection->>useReducedMotionHook: useReducedMotion()
  useReducedMotionHook-->>FeaturedCoursesSection: prefersReducedMotion
  FeaturedCoursesSection->>FeaturedCoursesSection: getAllCourses()
  FeaturedCoursesSection->>FeaturedCoursesSection: compute featuredCourses with internalAuditCourse and restaurantCourse
  FeaturedCoursesSection->>FramerMotion: AnimatePresence and motion.div with prefersReducedMotion
  FramerMotion-->>Browser: Render animated slide

  alt autoplay_enabled_and_not_prefersReducedMotion
    FeaturedCoursesSection->>FeaturedCoursesSection: useEffect setup autoplay interval (12000ms)
    loop every_12000ms
      FeaturedCoursesSection->>FeaturedCoursesSection: setCurrentIndex(next)
      FeaturedCoursesSection->>FramerMotion: animate slide transition
    end
  else prefersReducedMotion_or_autoplay_disabled
    FeaturedCoursesSection->>FeaturedCoursesSection: do not start interval
  end

  User->>Browser: Click nextSlide button
  Browser->>FeaturedCoursesSection: nextSlide()
  FeaturedCoursesSection->>FeaturedCoursesSection: setCurrentIndex(next)
  FeaturedCoursesSection->>FeaturedCoursesSection: setIsAutoPlaying(false)
  FeaturedCoursesSection->>FeaturedCoursesSection: setTimeout(enable autoplay after 18000ms)
  FeaturedCoursesSection->>FramerMotion: animate slide transition

  User->>Browser: Click dot indicator
  Browser->>FeaturedCoursesSection: goToSlide(index)
  FeaturedCoursesSection->>FeaturedCoursesSection: setCurrentIndex(index)
  FeaturedCoursesSection->>FeaturedCoursesSection: setIsAutoPlaying(false)
  FeaturedCoursesSection->>FeaturedCoursesSection: setTimeout(enable autoplay after 18000ms)
  FeaturedCoursesSection->>FramerMotion: animate slide transition

  User->>Browser: Click primary CTA ابدأ التعلم الآن
  Browser->>FeaturedCoursesSection: onClick handler
  FeaturedCoursesSection->>NextRouter: push(/courses/currentCourse.slug)
  NextRouter-->>CoursesPage: Render course details page

  User->>Browser: Click link استعرض جميع الدورات
  Browser->>NextRouter: Link navigation to /courses
  NextRouter-->>CoursesPage: Render all courses page
Loading

Class diagram for updated FeaturedCoursesSection structure

classDiagram
  class FeaturedCoursesSection {
    - number currentIndex
    - boolean isAutoPlaying
    - boolean prefersReducedMotion
    - Course[] allCourses
    - Course[] featuredCourses
    - Course currentCourse
    + FeaturedCoursesSection()
    + goToSlide(index number) void
    + nextSlide() void
    + prevSlide() void
  }

  class Course {
    + number id
    + string title
    + string slug
    + string pageUrl
    + string description
    + string category
    + string level
    + string duration
    + string price
    + number rating
    + number students
    + number lessons
    + number files
    + number videos
    + number audios
    + any[] modules
    + string image
  }

  class useReducedMotionHook {
    + useReducedMotion() boolean
  }

  class NextRouter {
    + push(path string) void
  }

  class FramerMotionComponents {
    + AnimatePresence
    + motion_div
    + motion_button
  }

  FeaturedCoursesSection --> Course : uses
  FeaturedCoursesSection --> useReducedMotionHook : calls
  FeaturedCoursesSection --> NextRouter : navigates_with
  FeaturedCoursesSection --> FramerMotionComponents : animates_with
Loading

Class diagram for updated CTASection with dual actions

classDiagram
  class CTASection {
    + CTASection()
  }

  class ROUTES {
    <<static>>
    + string REGISTER
    + string CONSULTING
  }

  class MotionWrapper {
    + MotionWrapper()
    + animation string
    + duration number
  }

  class NextLink {
    + href string
  }

  CTASection --> ROUTES : reads_paths
  CTASection --> MotionWrapper : wraps_content
  CTASection --> NextLink : renders_links_to_register_and_consulting
Loading

File-Level Changes

Change Details Files
Enhance featured courses slider with accessibility, reduced-motion support, richer stats, and a link to all courses.
  • Introduce useReducedMotion hook integration to disable autoplay and heavy animations when the user prefers reduced motion.
  • Refactor slider state handling and autoplay logic, including deriving currentCourse from the featured courses array and simplifying hooks dependencies.
  • Replace plain tags with next/image for course imagery, adding responsive sizing, fill layout, and higher-quality visual treatment.
  • Add ARIA attributes, roles, and focus-visible styles to the slider container, slides, navigation arrows, and pagination dots for better accessibility.
  • Extend slide content with rating, learners count, lessons/duration stats grid, and a short price subtitle, while keeping RTL layout and Arabic copy.
  • Add a footer link beneath the slider to navigate directly to the full courses listing.
src/components/homepage/FeaturedCoursesSection.tsx
Modernize the homepage CTA card with layered gradients, improved copy, and dual primary/secondary actions.
  • Wrap the CTA section in a background gradient layer and upgrade the card to a blurred, rounded, high-contrast surface with subtle decorative overlays.
  • Update headings and descriptive text to highlight accredited programs, interactive content, and personalized support while preserving Arabic RTL context.
  • Enhance the primary CTA button styling and accessibility, using Link instead of a nested button and adding focus-visible outlines.
  • Introduce a secondary consulting CTA link styled as an outlined button that directs users to consulting packages.
  • Add semantic ARIA labels and ids (aria-labelledby) to make the CTA region and heading more accessible.
src/components/homepage/CTASection.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link

coderabbitai bot commented Nov 25, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

شرح التغييرات

تحديثات تصميمية وتحسينات في مكونات الصفحة الرئيسية، حيث تم زيادة الحشو وتحسين التدرجات اللونية وتحديث أنماط البطاقات في قسم الدعوة للإجراء (CTA)، وإعادة هيكلة قسم الدورات المميزة لتضمين صور محسّنة وخطافات تفضيلات الحركة المخفضة والتفاصيل الدورات الغنية.

التغييرات

الفئة / الملفات ملخص التغييرات
تحديثات التصميم في قسم الدعوة للإجراء
src/components/homepage/CTASection.tsx
زيادة الحشو من py-8/py-10 إلى py-10/py-16 وإضافة سمة aria-labelledby. إدراج تدرج خلفية كعنصر aria-hidden. زيادة عرض حاوية المحتوى من max-w-4xl إلى max-w-5xl. تحديث أنماط البطاقات إلى rounded-3xl مع حدود شفافة وضبابية وتأثيرات الخلفية الداكنة. استبدال زر CTA واحد بإجراءين: رابط أساسي للتسجيل برابط ثانوي للاستشارة مع تأثيرات التركيز والتمرير.
إعادة هيكلة قسم الدورات المميزة
src/components/homepage/FeaturedCoursesSection.tsx
إضافة استيراد Next.js Image وأيقونة ArrowLeft. دمج خطاف تفضيل الحركة المخفضة (prefersReducedMotion) للوصول الشامل. إعادة هيكلة تدفق بيانات الدورات المميزة لحساب currentCourse من currentIndex. إضافة شارة رأس قسم جديدة وعنوان/عنوان فرعي محدث بالعربية. توسيع محتوى الشريحة لتضمين صورة وتفاصيل الدورة والفئة والمدة والتقييم والسعر ورابط CTA. إضافة شبكة إحصائيات سياقية وشريط رابط "استعرض جميع الدورات". تحسين مؤشرات النقاط مع مخططات تفصيلية والظلال والخصائص المرئية. تعديل السلوك المتحرك لاحترام الحركة المخفضة.

تقدير جهد مراجعة الكود

🎯 3 (متوسط) | ⏱️ ~25 دقيقة

المناطق التي قد تتطلب انتباهًا إضافيًا:

  • منطق الحركة المخفضة والتأثيرات الشرطية في FeaturedCoursesSection - التأكد من الاختبار الصحيح عبر أنماط التقليل
  • إعادة حساب currentCourse من currentIndex - التحقق من معالجة الحالات الحدية والتجديد الصحيح
  • أنماط البطاقات والخلفيات الجديدة في CTASection - التحقق من التوافق عبر أوضاع الإضاءة والعتمة
  • خصائص ARIA والوصول الشامل المضافة - التحقق من صحة الإنشاء والتوافق

القصيدة

🐰 يا لهذه الدعوات التي تتوهج بتدرجات جميلة!
والدورات الآن تظهر بكل تفاصيلها اللطيفة،
ألوان جديدة وحركات تحترم راحتنا،
أزرار براقة وصور محسّنة تجعل القلب يرقص!
هذا التصميم يشدو بنغمة المستخدم السعيد! 🌟✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed العنوان يعكس بدقة التغييرات الرئيسية في الطلب - تحديث قسم الدورات المميزة وقسم الحث على الإجراء (CTA).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/conduct-comprehensive-frontend-audit

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

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@deepsource-io
Copy link
Contributor

deepsource-io bot commented Nov 25, 2025

Here's the code health analysis summary for commits 00db202..edef020. View details on DeepSource ↗.

Analysis Summary

AnalyzerStatusSummaryLink
DeepSource JavaScript LogoJavaScript❌ Failure
❗ 5 occurences introduced
🎯 12 occurences resolved
View Check ↗
DeepSource Ansible LogoAnsible✅ SuccessView Check ↗

💡 If you’re a repository administrator, you can configure the quality gates from the settings.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @djharga, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the homepage's user interface and experience by updating two key sections: the featured courses slider and the main Call-to-Action card. The changes focus on improving visual appeal, providing more comprehensive information to users, and ensuring better accessibility across various user preferences and devices.

Highlights

  • Featured Courses Slider Revamp: The featured courses slider has been refreshed with richer metadata, improved accessibility hooks, and support for reduced motion preferences.
  • Course Imagery and Information: Course images now utilize Next.js Image components for optimized loading, and a new course statistics grid has been added to display details like lessons, duration, and student count. A direct link to all courses is also included.
  • Call-to-Action (CTA) Card Modernization: The CTA card has been modernized with layered gradients, enhanced accessible links, and a new secondary action for consulting services.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@qodo-code-review
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No audit logs: The new UI interactions (auto-play toggles, slide navigation, and CTA clicks) are not
logged, but this may be acceptable since these are frontend display actions without clear
critical security relevance.

Referred Code
useEffect(() => {
  if (!isAutoPlaying || prefersReducedMotion || featuredCourses.length === 0) return;

  const interval = setInterval(() => {
    setCurrentIndex(prev => (prev + 1) % featuredCourses.length);
  }, 12000);

  return () => clearInterval(interval);
}, [isAutoPlaying, featuredCourses.length, prefersReducedMotion]);

const goToSlide = (index: number) => {
  setCurrentIndex(index);
  setIsAutoPlaying(false);
  setTimeout(() => setIsAutoPlaying(true), 18000);
};

const nextSlide = () => {
  setCurrentIndex(prev => (prev + 1) % featuredCourses.length);
  setIsAutoPlaying(false);
  setTimeout(() => setIsAutoPlaying(true), 18000);
};


 ... (clipped 6 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing fallbacks: The component assumes currentCourse has fields like image, rating, and students without
null/undefined guards or try/catch around data retrieval and routing, which could fail if
data is incomplete.

Referred Code
const currentCourse = featuredCourses[currentIndex];

return (
  <section className="relative py-12 lg:py-16 overflow-hidden" aria-labelledby="featured-courses-heading">
    <Container size="xl" className="relative z-10">
      {/* Header */}
      <ScrollAnimation direction="up" delay={0.1}>
        <div className="text-center mb-12 mt-4" dir="rtl">
          <div className="inline-flex items-center gap-3 px-4 py-2 rounded-full bg-primary-50 dark:bg-primary-900/40 border border-primary-100 dark:border-primary-800 text-primary-700 dark:text-primary-200 text-sm font-semibold">
            <span className="inline-block h-2 w-2 rounded-full bg-primary-500" aria-hidden />
            مختارات المنصة
          </div>
          <h2
            id="featured-courses-heading"
            className="mt-4 text-3xl md:text-4xl lg:text-5xl font-extrabold tracking-tight text-neutral-900 dark:text-white"
            style={{ fontFamily: "var(--font-noto-kufi-arabic), 'Noto Kufi Arabic', 'Cairo', sans-serif" }}
          >
            الدورات الأكثر طلباً
          </h2>
          <p className="text-lg text-neutral-600 dark:text-neutral-400 max-w-2xl mx-auto leading-relaxed mt-3">
            اكتشف أحدث الدورات والخدمات التدريبية المميزة مع ملخص سريع عن المدة والمستوى والتقييم.


 ... (clipped 108 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Data assumptions: The UI renders external course data (title, description, image path) without explicit
validation or sanitization in this layer, which may be fine if upstream sources are
trusted and sanitized.

Referred Code
{currentCourse && (
  <GlassCard
    variant="elevated"
    size="lg"
    className="grid lg:grid-cols-2 gap-10 items-center justify-center p-8 lg:p-14 backdrop-blur-xl"
    aria-label={`دورة ${currentCourse.title}`}
  >
    <div className="relative group focus-within:ring-4 focus-within:ring-primary-200 focus-within:ring-offset-2 focus-within:ring-offset-transparent rounded-3xl">
      <div className="relative h-[320px] lg:h-[420px] rounded-3xl overflow-hidden bg-gradient-to-br from-neutral-200 to-neutral-300 dark:from-neutral-700 dark:to-neutral-800 shadow-xl">
        <Image
          src={currentCourse.image}
          alt={currentCourse.title}
          fill
          sizes="(min-width: 1024px) 540px, 100vw"
          className="object-cover brightness-105 contrast-110 transition-transform duration-500 group-hover:scale-105"
          priority
        />
        <div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent" aria-hidden />

        <div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300">
          <motion.div


 ... (clipped 72 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@codeant-ai codeant-ai bot added the size:L This PR changes 100-499 lines, ignoring generated files label Nov 25, 2025
@sonarqubecloud
Copy link

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • The autoplay restart logic in goToSlide/nextSlide/prevSlide sets timeouts without any cleanup, which can cause stray timers after unmount or rapid navigation; consider storing the timeout IDs in refs and clearing them in a cleanup effect or before scheduling a new one.
  • You already respect prefersReducedMotion for the main slide transition, but the framer-motion whileHover/whileTap animations on buttons and dots still run; consider disabling or simplifying these animations when prefersReducedMotion is true for a more consistent accessible experience.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The autoplay restart logic in goToSlide/nextSlide/prevSlide sets timeouts without any cleanup, which can cause stray timers after unmount or rapid navigation; consider storing the timeout IDs in refs and clearing them in a cleanup effect or before scheduling a new one.
- You already respect prefersReducedMotion for the main slide transition, but the framer-motion whileHover/whileTap animations on buttons and dots still run; consider disabling or simplifying these animations when prefersReducedMotion is true for a more consistent accessible experience.

## Individual Comments

### Comment 1
<location> `src/components/homepage/FeaturedCoursesSection.tsx:83-86` </location>
<code_context>
-  }, [isAutoPlaying, featuredCourses.length, currentIndex]);
+  }, [isAutoPlaying, featuredCourses.length, prefersReducedMotion]);

   const goToSlide = (index: number) => {
     setCurrentIndex(index);
     setIsAutoPlaying(false);
-    setTimeout(() => setIsAutoPlaying(true), 18000); // Resume auto-play after 18 seconds
+    setTimeout(() => setIsAutoPlaying(true), 18000);
   };

</code_context>

<issue_to_address>
**suggestion (bug_risk):** Consider centralizing and cleaning up the auto-play restart timeout to avoid multiple overlapping timers.

Right now each navigation handler starts a new timeout without cancelling the previous one, so rapid interactions can leave multiple pending timers that flip `isAutoPlaying` back to `true` at different times. To keep behavior predictable, store the timeout ID in a `useRef`, clear it at the start of each handler (and in a `useEffect` cleanup on unmount), then schedule a single new timeout.

Suggested implementation:

```typescript
import { useMemo, useState, useEffect, useRef } from 'react';

```

```typescript
    const interval = setInterval(() => {
      setCurrentIndex(prev => (prev + 1) % featuredCourses.length);
    }, 12000);

    return () => {
      clearInterval(interval);
      if (autoPlayTimeoutRef.current) {
        clearTimeout(autoPlayTimeoutRef.current);
      }
    };
  }, [isAutoPlaying, featuredCourses.length, prefersReducedMotion]);

```

```typescript
  const goToSlide = (index: number) => {
    setCurrentIndex(index);

    if (autoPlayTimeoutRef.current) {
      clearTimeout(autoPlayTimeoutRef.current);
    }

    setIsAutoPlaying(false);
    autoPlayTimeoutRef.current = setTimeout(() => {
      setIsAutoPlaying(true);
    }, 18000);
  };

```

To fully implement the centralized auto-play restart timeout, you should also:
1) Declare the ref once in the component body alongside your other hooks:
   - Add: `const autoPlayTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);`
   - Place it near your other state/hooks (e.g., after `const [isAutoPlaying, setIsAutoPlaying] = useState(true);` or similar).
2) Update any other navigation handlers that currently call `setTimeout(() => setIsAutoPlaying(true), 18000);` directly (e.g., `nextSlide`, `prevSlide`, dot-click handlers) to use the same ref pattern as `goToSlide`:
   - At the start of each handler, clear an existing timeout if present:
     `if (autoPlayTimeoutRef.current) { clearTimeout(autoPlayTimeoutRef.current); }`
   - Then schedule the new timeout and store it:
     `autoPlayTimeoutRef.current = setTimeout(() => { setIsAutoPlaying(true); }, 18000);`
This will ensure there is at most one pending timeout at any time, avoiding overlapping timers and keeping the auto-play behavior predictable.
</issue_to_address>

### Comment 2
<location> `src/components/homepage/CTASection.tsx:41` </location>
<code_context>
-                    <span>ابدأ الآن</span>
-                    <ArrowLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform duration-300" />
-                  </button>
+              <div className="flex flex-col lg:flex-row items-center justify-center gap-4 pt-2" aria-label="خيارات التسجيل">
+                <Link
+                  href={ROUTES.REGISTER}
</code_context>

<issue_to_address>
**issue:** The aria-label on this container may not be picked up by screen readers without a corresponding role.

Since this `div` only has an `aria-label` and no explicit role, some screen readers may not announce it. If you want this to be a named group for the CTA links, consider adding `role="group"` here, or moving the label onto an element with a more appropriate landmark/role so it’s consistently exposed to assistive technologies.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 83 to +86
setCurrentIndex(index);
setIsAutoPlaying(false);
setTimeout(() => setIsAutoPlaying(true), 18000); // Resume auto-play after 18 seconds
setTimeout(() => setIsAutoPlaying(true), 18000);
Copy link

Choose a reason for hiding this comment

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

suggestion (bug_risk): Consider centralizing and cleaning up the auto-play restart timeout to avoid multiple overlapping timers.

Right now each navigation handler starts a new timeout without cancelling the previous one, so rapid interactions can leave multiple pending timers that flip isAutoPlaying back to true at different times. To keep behavior predictable, store the timeout ID in a useRef, clear it at the start of each handler (and in a useEffect cleanup on unmount), then schedule a single new timeout.

Suggested implementation:

import { useMemo, useState, useEffect, useRef } from 'react';
    const interval = setInterval(() => {
      setCurrentIndex(prev => (prev + 1) % featuredCourses.length);
    }, 12000);

    return () => {
      clearInterval(interval);
      if (autoPlayTimeoutRef.current) {
        clearTimeout(autoPlayTimeoutRef.current);
      }
    };
  }, [isAutoPlaying, featuredCourses.length, prefersReducedMotion]);
  const goToSlide = (index: number) => {
    setCurrentIndex(index);

    if (autoPlayTimeoutRef.current) {
      clearTimeout(autoPlayTimeoutRef.current);
    }

    setIsAutoPlaying(false);
    autoPlayTimeoutRef.current = setTimeout(() => {
      setIsAutoPlaying(true);
    }, 18000);
  };

To fully implement the centralized auto-play restart timeout, you should also:

  1. Declare the ref once in the component body alongside your other hooks:
    • Add: const autoPlayTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
    • Place it near your other state/hooks (e.g., after const [isAutoPlaying, setIsAutoPlaying] = useState(true); or similar).
  2. Update any other navigation handlers that currently call setTimeout(() => setIsAutoPlaying(true), 18000); directly (e.g., nextSlide, prevSlide, dot-click handlers) to use the same ref pattern as goToSlide:
    • At the start of each handler, clear an existing timeout if present:
      if (autoPlayTimeoutRef.current) { clearTimeout(autoPlayTimeoutRef.current); }
    • Then schedule the new timeout and store it:
      autoPlayTimeoutRef.current = setTimeout(() => { setIsAutoPlaying(true); }, 18000);
      This will ensure there is at most one pending timeout at any time, avoiding overlapping timers and keeping the auto-play behavior predictable.

<span>ابدأ الآن</span>
<ArrowLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform duration-300" />
</button>
<div className="flex flex-col lg:flex-row items-center justify-center gap-4 pt-2" aria-label="خيارات التسجيل">
Copy link

Choose a reason for hiding this comment

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

issue: The aria-label on this container may not be picked up by screen readers without a corresponding role.

Since this div only has an aria-label and no explicit role, some screen readers may not announce it. If you want this to be a named group for the CTA links, consider adding role="group" here, or moving the label onto an element with a more appropriate landmark/role so it’s consistently exposed to assistive technologies.

@qodo-code-review
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Decouple featured courses from frontend

The selection logic for featured courses is currently hardcoded in the
FeaturedCoursesSection component. It should be refactored to be data-driven, for
instance by filtering courses that have a 'featured' attribute, to improve
flexibility and ease of updates.

Examples:

src/components/homepage/FeaturedCoursesSection.tsx [29-71]
  const featuredCourses = useMemo(() => {
    const reviewCourse = allCourses.find(course =>
      course.slug === 'internal-audit' ||
      course.title.includes('المراجعة الداخلية') ||
      course.title.includes('مراجعة داخلية')
    );

    const internalAuditCourse: Course = reviewCourse || {
      id: 998,
      title: 'احترف مهنة المراجعة الداخلية',

 ... (clipped 33 lines)

Solution Walkthrough:

Before:

// FeaturedCoursesSection.tsx
const FeaturedCoursesSection = () => {
  const allCourses = getAllCourses();

  const featuredCourses = useMemo(() => {
    // Hardcoded logic to find specific courses by slug or title
    const reviewCourse = allCourses.find(c => c.slug === 'internal-audit' || ...);
    const restaurantCourse = allCourses.find(c => c.slug === 'restaurant-management' || ...);
    
    // Manually create the list of featured courses
    const courses = [];
    courses.push(reviewCourse || { /* fallback object */ });
    if (restaurantCourse) {
      courses.push(restaurantCourse);
    }
    return courses;
  }, [allCourses]);

  // ... render slider with featuredCourses
};

After:

// data/courses/all-courses.ts (or similar data source)
// Add a 'featured' flag to course objects
export const courses = [
  { ..., slug: 'internal-audit', isFeatured: true },
  { ..., slug: 'restaurant-management', isFeatured: true },
  { ..., slug: 'another-course', isFeatured: false },
];

// FeaturedCoursesSection.tsx
const FeaturedCoursesSection = () => {
  const allCourses = getAllCourses();

  // Filter courses based on the data, not hardcoded logic
  const featuredCourses = useMemo(() => {
    return allCourses.filter(course => course.isFeatured);
  }, [allCourses]);

  // ... render slider with featuredCourses
};
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a significant architectural flaw where business logic is hardcoded in the frontend, severely impacting maintainability and flexibility, and proposes a superior data-driven approach.

High
Possible issue
Fix autoplay timer race condition

Fix a race condition in the autoplay slider by using a useRef to store and clear
the setTimeout ID, preventing multiple overlapping timers from being created.

src/components/homepage/FeaturedCoursesSection.tsx [73-81]

+const autoplayTimeoutRef = useRef<NodeJS.Timeout | null>(null);
+
 useEffect(() => {
-  if (!isAutoPlaying || prefersReducedMotion || featuredCourses.length === 0) return;
+  if (isAutoPlaying && !prefersReducedMotion && featuredCourses.length > 0) {
+    const interval = setInterval(() => {
+      setCurrentIndex(prev => (prev + 1) % featuredCourses.length);
+    }, 12000);
+    return () => clearInterval(interval);
+  }
 
-  const interval = setInterval(() => {
-    setCurrentIndex(prev => (prev + 1) % featuredCourses.length);
-  }, 12000);
-
-  return () => clearInterval(interval);
+  // Cleanup any pending timeout to re-enable autoplay
+  return () => {
+    if (autoplayTimeoutRef.current) {
+      clearTimeout(autoplayTimeoutRef.current);
+    }
+  };
 }, [isAutoPlaying, featuredCourses.length, prefersReducedMotion]);
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a race condition bug where multiple timeouts can be created, leading to unpredictable slider behavior, and proposes the standard React solution using useRef and a cleanup function.

Medium
Prevent multiple concurrent autoplay timeouts

Prevent multiple concurrent autoplay timeouts in navigation functions by using a
useRef to store the timeout ID and clearing any existing timeout before setting
a new one.

src/components/homepage/FeaturedCoursesSection.tsx [83-99]

 const goToSlide = (index: number) => {
+  if (autoplayTimeoutRef.current) clearTimeout(autoplayTimeoutRef.current);
   setCurrentIndex(index);
   setIsAutoPlaying(false);
-  setTimeout(() => setIsAutoPlaying(true), 18000);
+  autoplayTimeoutRef.current = setTimeout(() => setIsAutoPlaying(true), 18000);
 };
 
 const nextSlide = () => {
+  if (autoplayTimeoutRef.current) clearTimeout(autoplayTimeoutRef.current);
   setCurrentIndex(prev => (prev + 1) % featuredCourses.length);
   setIsAutoPlaying(false);
-  setTimeout(() => setIsAutoPlaying(true), 18000);
+  autoplayTimeoutRef.current = setTimeout(() => setIsAutoPlaying(true), 18000);
 };
 
 const prevSlide = () => {
+  if (autoplayTimeoutRef.current) clearTimeout(autoplayTimeoutRef.current);
   setCurrentIndex(prev => (prev - 1 + featuredCourses.length) % featuredCourses.length);
   setIsAutoPlaying(false);
-  setTimeout(() => setIsAutoPlaying(true), 18000);
+  autoplayTimeoutRef.current = setTimeout(() => setIsAutoPlaying(true), 18000);
 };
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a bug where rapid user interaction with navigation controls creates multiple conflicting timeouts, and it provides the correct fix using useRef to manage the timeout ID.

Medium
General
Make course image a clickable link

Improve user experience and accessibility by making the course image section a
clickable link that navigates to the course page, as its hover effect implies
interactivity.

src/components/homepage/FeaturedCoursesSection.tsx [144-166]

-<div className="relative group focus-within:ring-4 focus-within:ring-primary-200 focus-within:ring-offset-2 focus-within:ring-offset-transparent rounded-3xl">
+<Link
+  href={`/courses/${currentCourse.slug}`}
+  aria-label={`عرض تفاصيل دورة ${currentCourse.title}`}
+  className="relative block group focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary-300 dark:focus-visible:ring-primary-500 focus-visible:ring-offset-4 focus-visible:ring-offset-white dark:focus-visible:ring-offset-neutral-900 rounded-3xl"
+>
   <div className="relative h-[320px] lg:h-[420px] rounded-3xl overflow-hidden bg-gradient-to-br from-neutral-200 to-neutral-300 dark:from-neutral-700 dark:to-neutral-800 shadow-xl">
     <Image
       src={currentCourse.image}
       alt={currentCourse.title}
       fill
       sizes="(min-width: 1024px) 540px, 100vw"
       className="object-cover brightness-105 contrast-110 transition-transform duration-500 group-hover:scale-105"
       priority
     />
     <div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent" aria-hidden />
 
     <div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300">
       <motion.div
         initial={prefersReducedMotion ? { opacity: 1 } : { scale: 0.8, opacity: 0 }}
         whileHover={prefersReducedMotion ? undefined : { scale: 1.08 }}
         className="w-20 h-20 bg-white rounded-full flex items-center justify-center shadow-2xl"
       >
         <Play className="w-10 h-10 text-primary-600 fill-primary-600" />
       </motion.div>
     </div>
   </div>
-</div>
+</Link>
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out a UX issue where a non-interactive element appears clickable and provides a good solution by wrapping it in a Link, improving both user experience and accessibility.

Medium
  • More

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request provides a significant visual and functional refresh to the homepage's featured courses and CTA sections. The updates introduce richer course metadata, improved accessibility with ARIA attributes and reduced-motion support, and a migration to Next.js's optimized Image component. The code is well-structured, and the accessibility enhancements are particularly commendable. My feedback includes a few suggestions to improve UI consistency for RTL users and enhance code maintainability.

Comment on lines 73 to 99
useEffect(() => {
if (!isAutoPlaying || featuredCourses.length === 0) return;
if (!isAutoPlaying || prefersReducedMotion || featuredCourses.length === 0) return;

const interval = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % featuredCourses.length);
}, 12000); // Change slide every 12 seconds - slow enough for comfortable reading
setCurrentIndex(prev => (prev + 1) % featuredCourses.length);
}, 12000);

return () => clearInterval(interval);
}, [isAutoPlaying, featuredCourses.length, currentIndex]);
}, [isAutoPlaying, featuredCourses.length, prefersReducedMotion]);

const goToSlide = (index: number) => {
setCurrentIndex(index);
setIsAutoPlaying(false);
setTimeout(() => setIsAutoPlaying(true), 18000); // Resume auto-play after 18 seconds
setTimeout(() => setIsAutoPlaying(true), 18000);
};

const nextSlide = () => {
setCurrentIndex((prev) => (prev + 1) % featuredCourses.length);
setCurrentIndex(prev => (prev + 1) % featuredCourses.length);
setIsAutoPlaying(false);
setTimeout(() => setIsAutoPlaying(true), 18000);
};

const prevSlide = () => {
setCurrentIndex((prev) => (prev - 1 + featuredCourses.length) % featuredCourses.length);
setCurrentIndex(prev => (prev - 1 + featuredCourses.length) % featuredCourses.length);
setIsAutoPlaying(false);
setTimeout(() => setIsAutoPlaying(true), 18000);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The timeout and interval durations (12000 and 18000) are used as magic numbers in this block. To improve readability and maintainability, it's best to extract them into named constants at the top of the component, for example:

const AUTOPLAY_INTERVAL_MS = 12000;
const AUTOPLAY_RESUME_DELAY_MS = 18000;

Then you can use these constants within the useEffect and slide handler functions.

},
].map((stat, index) => (
<div
key={`${stat.label}-${index}`}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Using index as part of the key prop is generally discouraged in React, as it can lead to subtle bugs if the list order changes. Since stat.label is unique for each item in this static list, using it directly as the key is a more robust and idiomatic approach.

Suggested change
key={`${stat.label}-${index}`}
key={stat.label}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/homepage/FeaturedCoursesSection.tsx (1)

26-71: مشكلة في أداء useMemo - مرجع جديد في كل render

getAllCourses() يُنشئ مصفوفة جديدة في كل render (السطر 26)، مما يجعل allCourses مرجعًا مختلفًا في كل مرة. هذا يُبطل فائدة useMemo لأن المصفوفة ستُعاد حسابها دائمًا.

اقتراح الإصلاح:

-  const allCourses = getAllCourses();
+  const allCourses = useMemo(() => getAllCourses(), []);

   // الحصول على دورات محددة...
   const featuredCourses = useMemo(() => {
     // ...
-  }, [allCourses]);
+  }, [allCourses]);

أو دمج كل شيء في useMemo واحد:

-  const allCourses = getAllCourses();
-
-  const featuredCourses = useMemo(() => {
-    const reviewCourse = allCourses.find(...)
+  const featuredCourses = useMemo(() => {
+    const allCourses = getAllCourses();
+    const reviewCourse = allCourses.find(...)
     // ...
-  }, [allCourses]);
+  }, []);
🧹 Nitpick comments (4)
src/components/homepage/FeaturedCoursesSection.tsx (2)

83-99: احتمال تسرب ذاكرة مع setTimeout

دوال التنقل تستخدم setTimeout بدون تنظيف. إذا تم إلغاء تحميل المكون قبل انتهاء المؤقت (18 ثانية)، ستحاول الـ callback تحديث حالة مكون غير موجود.

اقتراح استخدام useRef لتتبع المؤقت وتنظيفه:

+  const autoPlayTimeoutRef = useRef<NodeJS.Timeout | null>(null);
+
+  useEffect(() => {
+    return () => {
+      if (autoPlayTimeoutRef.current) {
+        clearTimeout(autoPlayTimeoutRef.current);
+      }
+    };
+  }, []);

   const goToSlide = (index: number) => {
     setCurrentIndex(index);
     setIsAutoPlaying(false);
-    setTimeout(() => setIsAutoPlaying(true), 18000);
+    if (autoPlayTimeoutRef.current) clearTimeout(autoPlayTimeoutRef.current);
+    autoPlayTimeoutRef.current = setTimeout(() => setIsAutoPlaying(true), 18000);
   };

109-112: إصلاح صياغة JSX للعنصر span

العنصر span في السطر 110 يجب أن يكون ذاتي الإغلاق بشكل صحيح مع قيمة صريحة لـ aria-hidden. هذا يتوافق مع تحذير SonarCloud.

-              <span className="inline-block h-2 w-2 rounded-full bg-primary-500" aria-hidden />
+              <span className="inline-block h-2 w-2 rounded-full bg-primary-500" aria-hidden="true" />
               مختارات المنصة
src/components/homepage/CTASection.tsx (2)

29-32: إصلاح صياغة aria-hidden للعنصر span

مشابه للملف الأول، يجب إضافة قيمة صريحة لـ aria-hidden. هذا يتوافق مع تحذير SonarCloud.

-                <span className="h-2 w-2 rounded-full bg-primary-500" aria-hidden />
+                <span className="h-2 w-2 rounded-full bg-primary-500" aria-hidden="true" />

41-55: إزالة aria-label غير الضرورية من div

aria-label على عنصر div غير دلالي لا تُقرأ من قارئات الشاشة بشكل موثوق. الروابط نفسها واضحة ولا تحتاج لتسمية إضافية على الحاوية.

-              <div className="flex flex-col lg:flex-row items-center justify-center gap-4 pt-2" aria-label="خيارات التسجيل">
+              <div className="flex flex-col lg:flex-row items-center justify-center gap-4 pt-2">

تنفيذ أزرار CTA مع focus-visible ممتاز لإمكانية الوصول بلوحة المفاتيح.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00db202 and edef020.

📒 Files selected for processing (2)
  • src/components/homepage/CTASection.tsx (1 hunks)
  • src/components/homepage/FeaturedCoursesSection.tsx (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-25T19:42:26.188Z
Learnt from: CR
Repo: djharga/khatwa-learning-platform PR: 0
File: .clinerules/AI_INSTRUCTIONS.md:0-0
Timestamp: 2025-10-25T19:42:26.188Z
Learning: Applies to src/{components,app}/**/*.tsx : Meet WCAG 2.1 AA accessibility at minimum

Applied to files:

  • src/components/homepage/FeaturedCoursesSection.tsx
  • src/components/homepage/CTASection.tsx
🪛 GitHub Check: SonarCloud Code Analysis
src/components/homepage/FeaturedCoursesSection.tsx

[warning] 110-110: Ambiguous spacing after previous element span

See more on https://sonarcloud.io/project/issues?id=djharga_khatwa-learning-platform&issues=AZq442CpBlbkNXtvuH-o&open=AZq442CpBlbkNXtvuH-o&pullRequest=25


[warning] 126-126: Use

, or
instead of the "region" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=djharga_khatwa-learning-platform&issues=AZq442CpBlbkNXtvuH-p&open=AZq442CpBlbkNXtvuH-p&pullRequest=25


[warning] 215-219: Use

  • instead of the "listitem" role to ensure accessibility across all devices.

    See more on https://sonarcloud.io/project/issues?id=djharga_khatwa-learning-platform&issues=AZq442CpBlbkNXtvuH-r&open=AZq442CpBlbkNXtvuH-r&pullRequest=25


    [warning] 205-205: Use

    ,
      , or
  • src/components/homepage/CTASection.tsx

    [warning] 30-30: Ambiguous spacing after previous element span

    See more on https://sonarcloud.io/project/issues?id=djharga_khatwa-learning-platform&issues=AZq442AOBlbkNXtvuH-n&open=AZq442AOBlbkNXtvuH-n&pullRequest=25

    ⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
    • GitHub Check: Redirect rules - scintillating-centaur-5d4290
    • GitHub Check: Redirect rules - khatw
    • GitHub Check: Sourcery review
    • GitHub Check: Header rules - khatw
    • GitHub Check: Header rules - scintillating-centaur-5d4290
    • GitHub Check: Pages changed - khatw
    • GitHub Check: Pages changed - scintillating-centaur-5d4290
    🔇 Additional comments (7)
    src/components/homepage/FeaturedCoursesSection.tsx (5)

    1-14: استيرادات مناسبة وتحسينات جيدة

    استخدام Image من Next.js لتحسين الصور و useReducedMotion لدعم إمكانية الوصول هي ممارسات ممتازة.


    20-24: دعم ممتاز لتقليل الحركة

    استخدام useReducedMotion لاحترام تفضيلات المستخدم يتوافق مع معايير WCAG 2.1 AA. بناءً على التعليمات المسترجعة.


    73-81: تنفيذ جيد للتشغيل التلقائي

    منطق useEffect يحترم تفضيلات تقليل الحركة ويتعامل بشكل صحيح مع المصفوفة الفارغة.


    266-283: تنفيذ ممتاز لأزرار التنقل

    استخدام aria-pressed لحالة التبديل و focus-visible للتركيز بلوحة المفاتيح يوفر تجربة وصول جيدة.


    285-293: إضافة جيدة لرابط "جميع الدورات"

    رابط واضح مع أيقونة سهم وحالات hover/focus مناسبة.

    src/components/homepage/CTASection.tsx (2)

    16-17: تحسينات جيدة لإمكانية الوصول

    استخدام aria-labelledby للقسم و aria-hidden للعناصر الزخرفية ممارسة جيدة. يُفضل إضافة قيمة صريحة aria-hidden="true" للوضوح.


    33-40: هيكل العنوان صحيح

    ربط id="cta-heading" مع aria-labelledby في القسم يوفر ملاحة شاشة واضحة.

    Comment on lines +126 to +127
    <div className="relative" role="region" aria-live="polite" aria-roledescription="شريط الدورات المميزة">
    <div className="relative overflow-hidden rounded-3xl min-h-[520px]">
    Copy link

    Choose a reason for hiding this comment

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

    🛠️ Refactor suggestion | 🟠 Major

    استخدام HTML دلالي بدلاً من role="region"

    وفقًا لتحليل SonarCloud ومعايير WCAG 2.1 AA، يُفضل استخدام <section> مع aria-label بدلاً من div مع role="region".

    -          <div className="relative" role="region" aria-live="polite" aria-roledescription="شريط الدورات المميزة">
    +          <section className="relative" aria-label="شريط الدورات المميزة" aria-live="polite">

    وفي نهاية القسم (قبل السطر 294):

    -        </div>
    +        </section>

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

    🧰 Tools
    🪛 GitHub Check: SonarCloud Code Analysis

    [warning] 126-126: Use

    , or
    instead of the "region" role to ensure accessibility across all devices.

    See more on https://sonarcloud.io/project/issues?id=djharga_khatwa-learning-platform&issues=AZq442CpBlbkNXtvuH-p&open=AZq442CpBlbkNXtvuH-p&pullRequest=25

    🤖 Prompt for AI Agents
    In src/components/homepage/FeaturedCoursesSection.tsx around lines 126-127 (and
    close the section before line 294), replace the outer div that uses
    role="region" with a semantic <section> element and move the aria attributes to
    it: keep the same className and aria-live="polite" and aria-roledescription but
    use aria-label="شريط الدورات المميزة" on the section; also update the closing
    tag at the end of the section (before line 294) from </div> to </section> so the
    markup is semantic and preserves the same accessibility attributes and styling.
    

    Comment on lines +205 to +229
    <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 pt-2" role="list">
    {[
    { icon: <BookOpen className="w-4 h-4" />, label: 'الوحدات', value: `${currentCourse.lessons} درس` },
    { icon: <Clock className="w-4 h-4" />, label: 'المدة', value: currentCourse.duration },
    {
    icon: <Users className="w-4 h-4" />,
    label: 'المتعلمون',
    value: `${currentCourse.students.toLocaleString()} متدرب`,
    },
    ].map((stat, index) => (
    <div
    key={`${stat.label}-${index}`}
    className="flex items-center gap-3 rounded-xl border border-neutral-200/80 dark:border-neutral-700/80 bg-neutral-50/70 dark:bg-neutral-900/40 px-4 py-3"
    role="listitem"
    >
    <span className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary-50 dark:bg-primary-900/30 text-primary-600 dark:text-primary-200">
    {stat.icon}
    </span>
    <div className="text-right">
    <p className="text-xs text-neutral-500">{stat.label}</p>
    <p className="text-sm font-semibold text-neutral-900 dark:text-white">{stat.value}</p>
    </div>
    </div>
    ))}
    </div>
    Copy link

    Choose a reason for hiding this comment

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

    🛠️ Refactor suggestion | 🟠 Major

    استخدام عناصر القائمة الدلالية <ul> و <li>

    وفقًا لتحليل SonarCloud ومتطلبات WCAG 2.1 AA، يجب استخدام عناصر HTML الدلالية بدلاً من أدوار ARIA.

    -                      <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 pt-2" role="list">
    +                      <ul className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 pt-2 list-none">
                             {[
                               // ...stats array
                             ].map((stat, index) => (
    -                          <div
    +                          <li
                                 key={`${stat.label}-${index}`}
                                 className="flex items-center gap-3 rounded-xl border border-neutral-200/80 dark:border-neutral-700/80 bg-neutral-50/70 dark:bg-neutral-900/40 px-4 py-3"
    -                            role="listitem"
                               >
                                 {/* ...content */}
    -                          </div>
    +                          </li>
                             ))}
    -                      </div>
    +                      </ul>
    📝 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
    <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 pt-2" role="list">
    {[
    { icon: <BookOpen className="w-4 h-4" />, label: 'الوحدات', value: `${currentCourse.lessons} درس` },
    { icon: <Clock className="w-4 h-4" />, label: 'المدة', value: currentCourse.duration },
    {
    icon: <Users className="w-4 h-4" />,
    label: 'المتعلمون',
    value: `${currentCourse.students.toLocaleString()} متدرب`,
    },
    ].map((stat, index) => (
    <div
    key={`${stat.label}-${index}`}
    className="flex items-center gap-3 rounded-xl border border-neutral-200/80 dark:border-neutral-700/80 bg-neutral-50/70 dark:bg-neutral-900/40 px-4 py-3"
    role="listitem"
    >
    <span className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary-50 dark:bg-primary-900/30 text-primary-600 dark:text-primary-200">
    {stat.icon}
    </span>
    <div className="text-right">
    <p className="text-xs text-neutral-500">{stat.label}</p>
    <p className="text-sm font-semibold text-neutral-900 dark:text-white">{stat.value}</p>
    </div>
    </div>
    ))}
    </div>
    <ul className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 pt-2 list-none">
    {[
    { icon: <BookOpen className="w-4 h-4" />, label: 'الوحدات', value: `${currentCourse.lessons} درس` },
    { icon: <Clock className="w-4 h-4" />, label: 'المدة', value: currentCourse.duration },
    {
    icon: <Users className="w-4 h-4" />,
    label: 'المتعلمون',
    value: `${currentCourse.students.toLocaleString()} متدرب`,
    },
    ].map((stat, index) => (
    <li
    key={`${stat.label}-${index}`}
    className="flex items-center gap-3 rounded-xl border border-neutral-200/80 dark:border-neutral-700/80 bg-neutral-50/70 dark:bg-neutral-900/40 px-4 py-3"
    >
    <span className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary-50 dark:bg-primary-900/30 text-primary-600 dark:text-primary-200">
    {stat.icon}
    </span>
    <div className="text-right">
    <p className="text-xs text-neutral-500">{stat.label}</p>
    <p className="text-sm font-semibold text-neutral-900 dark:text-white">{stat.value}</p>
    </div>
    </li>
    ))}
    </ul>
    🧰 Tools
    🪛 GitHub Check: SonarCloud Code Analysis

    [warning] 215-219: Use

  • instead of the "listitem" role to ensure accessibility across all devices.

    See more on https://sonarcloud.io/project/issues?id=djharga_khatwa-learning-platform&issues=AZq442CpBlbkNXtvuH-r&open=AZq442CpBlbkNXtvuH-r&pullRequest=25


    [warning] 205-205: Use

    ,
      , or
  • 🤖 Prompt for AI Agents
    In src/components/homepage/FeaturedCoursesSection.tsx around lines 205 to 229,
    the code uses divs with role="list" and role="listitem" but should use semantic
    <ul> and <li> elements for WCAG/sonar compliance; replace the outer div with a
    <ul> keeping the same className and remove role="list", and replace each mapped
    inner div with an <li> (keeping the existing key, className, contents and
    styling) and remove role="listitem"; ensure markup remains RTL/text-right and no
    other behavior changes.
    

    @codeant-ai
    Copy link

    codeant-ai bot commented Nov 25, 2025

    CodeAnt AI finished reviewing your PR.

    @djharga djharga self-assigned this Nov 25, 2025
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Labels

    codex Review effort 2/5 size:L This PR changes 100-499 lines, ignoring generated files

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    1 participant