Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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 · |
✅ Deploy Preview for scintillating-centaur-5d4290 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for khatw ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Reviewer's GuideRevamps 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 autoplaysequenceDiagram
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
Class diagram for updated FeaturedCoursesSection structureclassDiagram
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
Class diagram for updated CTASection with dual actionsclassDiagram
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
File-Level ChangesTips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
Note Other AI code review bot(s) detectedCodeRabbit 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)، وإعادة هيكلة قسم الدورات المميزة لتضمين صور محسّنة وخطافات تفضيلات الحركة المخفضة والتفاصيل الدورات الغنية. التغييرات
تقدير جهد مراجعة الكود🎯 3 (متوسط) | ⏱️ ~25 دقيقة المناطق التي قد تتطلب انتباهًا إضافيًا:
القصيدة
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
|
Here's the code health analysis summary for commits Analysis Summary
|
Summary of ChangesHello @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
Using Gemini Code AssistThe 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
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 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
|
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
||||||||||||||||||||||||
|
There was a problem hiding this comment.
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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| setCurrentIndex(index); | ||
| setIsAutoPlaying(false); | ||
| setTimeout(() => setIsAutoPlaying(true), 18000); // Resume auto-play after 18 seconds | ||
| setTimeout(() => setIsAutoPlaying(true), 18000); |
There was a problem hiding this comment.
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:
- 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).
- Add:
- 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 asgoToSlide:- 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.
- At the start of each handler, clear an existing timeout if present:
| <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="خيارات التسجيل"> |
There was a problem hiding this comment.
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.
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||||
There was a problem hiding this comment.
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.
| 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); | ||
| }; |
There was a problem hiding this comment.
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}`} |
There was a problem hiding this comment.
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.
| key={`${stat.label}-${index}`} | |
| key={stat.label} |
There was a problem hiding this comment.
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
📒 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.tsxsrc/components/homepage/CTASection.tsx
🪛 GitHub Check: SonarCloud Code Analysis
src/components/homepage/FeaturedCoursesSection.tsx
[warning] 110-110: Ambiguous spacing after previous element span
[warning] 126-126: Use
[warning] 215-219: Use
[warning] 205-205: Use
,- , or
- instead of the "list" role to ensure accessibility across all devices.
src/components/homepage/CTASection.tsx
[warning] 30-30: Ambiguous spacing after previous element span
⏰ 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في القسم يوفر ملاحة شاشة واضحة.
| <div className="relative" role="region" aria-live="polite" aria-roledescription="شريط الدورات المميزة"> | ||
| <div className="relative overflow-hidden rounded-3xl min-h-[520px]"> |
There was a problem hiding this comment.
🛠️ 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
🤖 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.
| <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> |
There was a problem hiding this comment.
🛠️ 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.
| <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
[warning] 205-205: Use
,- , or
- instead of the "list" role to ensure accessibility across all devices.
🤖 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 finished reviewing your PR. |



User description
User description
Summary
Testing
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
File Walkthrough
CTASection.tsx
Modernize CTA with gradients and accessibilitysrc/components/homepage/CTASection.tsx
aria-labelledby,aria-hidden, and focus-visible outlinesCTA
effects
certificates
FeaturedCoursesSection.tsx
Optimize slider with Next.js Image and accessibilitysrc/components/homepage/FeaturedCoursesSection.tsx
imgtags with Next.jsImagecomponent foroptimization and lazy loading
useReducedMotionhook to respect user motion preferences inanimations
student count
states on interactive elements
contrast
CodeAnt-AI Description
Refresh homepage CTA and featured courses highlights
What Changed
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:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
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:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
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.