feat: Implement Creator Dashboard metrics cards and quick actions#54
Conversation
📝 WalkthroughWalkthroughThis PR introduces MetricCard and QuickActions components to the dashboard, replacing static content with a responsive metrics grid that maps over a metrics array and displays metric cards dynamically, along with a new quick actions section containing action buttons. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. 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 |
|
@Luluameh waiting patiently for your review |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/components/dashboard/QuickActions.tsx (1)
6-10: Replaceconsole.logstubs with actual navigation.All three
onClickhandlers only log to the console, making the Quick Actions section non-functional. These should use Next.jsuseRouter(or<Link>) to route to the relevant pages.Additionally, the
actionsarray is re-created on every render. Moving it outside the component (or convertingonClickto router pushes inside the component) avoids this.♻️ Proposed refactor
"use client" -import React from "react" +import { useRouter } from "next/navigation" import { Button } from "@/components/ui/Button" +const ACTIONS: { label: string; href: string }[] = [ + { label: "Create Course", href: "/courses/create" }, + { label: "Viewing Earnings", href: "/earnings" }, + { label: "Student Feedback", href: "/feedback" }, +] + export default function QuickActions() { - const actions = [ - { label: "Create Course", onClick: () => console.log("Create Course clicked") }, - { label: "Viewing Earnings", onClick: () => console.log("Viewing Earnings clicked") }, - { label: "Student Feedback", onClick: () => console.log("Student Feedback clicked") }, - ] + const router = useRouter() return ( <div className="mt-8"> <h3 className="text-sm font-medium text-white mb-4">Quick Actions</h3> <div className="flex flex-wrap gap-4"> - {actions.map((action) => ( + {ACTIONS.map((action) => ( <Button key={action.label} variant="secondary" className="bg-[`#110719`] border-border/50 hover:bg-white/[0.05]" - onClick={action.onClick} + onClick={() => router.push(action.href)} > {action.label} </Button> ))} </div> </div> ) }Would you like me to open an issue to track replacing these stubs with real routing/action handlers?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/dashboard/QuickActions.tsx` around lines 6 - 10, The Quick Actions array currently uses console.log stubs and is recreated each render; replace the stubs with real Next.js navigation and avoid reallocating the array on every render: inside the QuickActions component import and call useRouter() to get router and update each action's onClick to call router.push('/courses/new'), router.push('/earnings') and router.push('/feedback') (or the correct route paths), and then either lift the actions array outside the component and store only path strings there, or build the actions via useMemo inside QuickActions so onClick closures capture the router without re-creating the array on every render.src/app/dashboard/page.tsx (2)
7-36: All metric values are hardcoded; externalize for dynamic data.The PR objective explicitly calls for a "flexible structure for dynamic data." The
MetricCardcomponent is already structured for it, but the data source ($1,500,12,10+) is entirely static. Before this connects to real data, themetricsarray should at minimum be derived from a prop, a context value, or an async data fetch (e.g.,fetchin a parent Server Component that passes values down).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/dashboard/page.tsx` around lines 7 - 36, The metrics array in page.tsx is hardcoded; change its sourcing so values are dynamic by fetching or receiving data and then mapping into the same shape used by MetricCard; specifically, replace the static const metrics with a function or prop-derived value (e.g., accept a prop like dashboardMetrics or call an async fetch in the server component) that returns an array of objects with label, value, icon, iconColor, and iconBgColor, and then pass that array to where MetricCard is rendered (keep the MetricCard props/names intact so only the data source changes).
12-13: Prefer Tailwind semantic color utilities over hardcoded hex values.
#22d3ee,#9d50ff, and#ffa500are magic hex strings scattered across four metric entries. If the palette changes, every entry needs a manual update. Tailwind's built-in utilities (text-cyan-400,text-purple-500,text-amber-500) or project-level CSS variables would centralise the tokens.♻️ Example for the first two metrics
- iconColor: "text-[`#22d3ee`]", - iconBgColor: "bg-[`#22d3ee`]/10", + iconColor: "text-cyan-400", + iconBgColor: "bg-cyan-400/10", ... - iconColor: "text-[`#9d50ff`]", - iconBgColor: "bg-[`#9d50ff`]/10", + iconColor: "text-purple-500", + iconBgColor: "bg-purple-500/10",Also applies to: 19-20, 26-27, 33-34
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/dashboard/page.tsx` around lines 12 - 13, The metric objects use hardcoded hex strings for iconColor and iconBgColor (e.g., "text-[`#22d3ee`]" / "bg-[`#22d3ee`]/10"); update these to use Tailwind semantic utilities (for example replace "text-[`#22d3ee`]" with "text-cyan-400" and "bg-[`#22d3ee`]/10" with "bg-cyan-400/10") across all metric entries where iconColor and iconBgColor are set (the metric objects in page.tsx), and similarly swap "#9d50ff" -> "text-purple-500"/"bg-purple-500/10" and "#ffa500" -> "text-amber-500"/"bg-amber-500/10" so the tokens are semantic and centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/dashboard/page.tsx`:
- Line 2: The imported icon choices are semantically wrong and duplicate
visuals: replace Percent with a money-related icon (e.g., DollarSign or
Banknote) for the "Total Earnings" card, and avoid using Briefcase for both
"Number of Enrolled Students" and "Total Courses Published" by swapping one to a
student-related icon (e.g., Users or GraduationCap) and the other to a
course-related icon (e.g., BookOpen or LayoutGrid); update the import list to
include the chosen icons (DollarSign/Banknote, Users/GraduationCap,
BookOpen/LayoutGrid) and remove or replace Briefcase accordingly so the
components rendering those cards use the new icon identifiers.
In `@src/components/dashboard/MetricCard.tsx`:
- Around line 24-34: Wrap the metric text content in an accessible grouping so
screen readers associate the value with its label: inside the MetricCard
component (the returned JSX where {value} and {label} are rendered), replace the
two sibling divs with a semantic grouping such as a <dl> containing <dt> for the
label and <dd> for the value (or alternatively add an explicit
aria-label/aria-labelledby on the wrapping div), and ensure the class names
(text-2xl font-bold text-white and text-sm text-slate-400) are preserved on the
corresponding dt/dd so styling stays the same.
---
Nitpick comments:
In `@src/app/dashboard/page.tsx`:
- Around line 7-36: The metrics array in page.tsx is hardcoded; change its
sourcing so values are dynamic by fetching or receiving data and then mapping
into the same shape used by MetricCard; specifically, replace the static const
metrics with a function or prop-derived value (e.g., accept a prop like
dashboardMetrics or call an async fetch in the server component) that returns an
array of objects with label, value, icon, iconColor, and iconBgColor, and then
pass that array to where MetricCard is rendered (keep the MetricCard props/names
intact so only the data source changes).
- Around line 12-13: The metric objects use hardcoded hex strings for iconColor
and iconBgColor (e.g., "text-[`#22d3ee`]" / "bg-[`#22d3ee`]/10"); update these to
use Tailwind semantic utilities (for example replace "text-[`#22d3ee`]" with
"text-cyan-400" and "bg-[`#22d3ee`]/10" with "bg-cyan-400/10") across all metric
entries where iconColor and iconBgColor are set (the metric objects in
page.tsx), and similarly swap "#9d50ff" -> "text-purple-500"/"bg-purple-500/10"
and "#ffa500" -> "text-amber-500"/"bg-amber-500/10" so the tokens are semantic
and centralized.
In `@src/components/dashboard/QuickActions.tsx`:
- Around line 6-10: The Quick Actions array currently uses console.log stubs and
is recreated each render; replace the stubs with real Next.js navigation and
avoid reallocating the array on every render: inside the QuickActions component
import and call useRouter() to get router and update each action's onClick to
call router.push('/courses/new'), router.push('/earnings') and
router.push('/feedback') (or the correct route paths), and then either lift the
actions array outside the component and store only path strings there, or build
the actions via useMemo inside QuickActions so onClick closures capture the
router without re-creating the array on every render.
| @@ -1,8 +1,56 @@ | |||
| "use client" | |||
| import { Percent, Briefcase, MessageSquare } from "lucide-react" | |||
There was a problem hiding this comment.
Wrong icon for "Total Earnings"; duplicate icon for "Students" and "Courses".
- Line 11:
Percentrenders a%symbol, which is semantically misleading for a monetary earnings metric.DollarSign,Banknote, orTrendingUpwould better convey the intent. - Lines 18 and 25:
Briefcaseis used for both "Number of Enrolled Students" and "Total Courses Published". Cards with identical icons are visually indistinguishable at a glance. ConsiderUsers/GraduationCapfor students andBookOpen/LayoutGridfor courses.
🐛 Proposed icon corrections
-import { Percent, Briefcase, MessageSquare } from "lucide-react"
+import { DollarSign, Users, BookOpen, MessageSquare } from "lucide-react"
const metrics = [
{
label: "Total Earnings",
value: "$1,500",
- icon: Percent,
+ icon: DollarSign,
...
},
{
label: "Number of Enrolled Students",
value: "12",
- icon: Briefcase,
+ icon: Users,
...
},
{
label: "Total Courses Published",
value: "12",
- icon: Briefcase,
+ icon: BookOpen,
...
},
...
]Also applies to: 11-11, 18-18, 25-25
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/dashboard/page.tsx` at line 2, The imported icon choices are
semantically wrong and duplicate visuals: replace Percent with a money-related
icon (e.g., DollarSign or Banknote) for the "Total Earnings" card, and avoid
using Briefcase for both "Number of Enrolled Students" and "Total Courses
Published" by swapping one to a student-related icon (e.g., Users or
GraduationCap) and the other to a course-related icon (e.g., BookOpen or
LayoutGrid); update the import list to include the chosen icons
(DollarSign/Banknote, Users/GraduationCap, BookOpen/LayoutGrid) and remove or
replace Briefcase accordingly so the components rendering those cards use the
new icon identifiers.
| return ( | ||
| <Card className={cn("p-6 flex items-center space-x-4", className)}> | ||
| <div className={cn("p-3 rounded-full flex-shrink-0", iconBgColor)}> | ||
| <Icon className={cn("size-6", iconColor)} /> | ||
| </div> | ||
| <div> | ||
| <div className="text-2xl font-bold text-white">{value}</div> | ||
| <div className="text-sm text-slate-400">{label}</div> | ||
| </div> | ||
| </Card> | ||
| ) |
There was a problem hiding this comment.
Metric value and label lack semantic grouping for screen readers.
The {value} and {label} are in sibling <div>s with no association, so a screen reader reads them as independent text nodes ("$1,500 … Total Earnings") with no explicit relationship. A definition list (<dl>/<dt>/<dd>) or an aria-label on the wrapping element would make the pairing explicit.
♿ Proposed accessibility improvement
- <div>
- <div className="text-2xl font-bold text-white">{value}</div>
- <div className="text-sm text-slate-400">{label}</div>
- </div>
+ <dl>
+ <dd className="text-2xl font-bold text-white">{value}</dd>
+ <dt className="text-sm text-slate-400">{label}</dt>
+ </dl>📝 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.
| return ( | |
| <Card className={cn("p-6 flex items-center space-x-4", className)}> | |
| <div className={cn("p-3 rounded-full flex-shrink-0", iconBgColor)}> | |
| <Icon className={cn("size-6", iconColor)} /> | |
| </div> | |
| <div> | |
| <div className="text-2xl font-bold text-white">{value}</div> | |
| <div className="text-sm text-slate-400">{label}</div> | |
| </div> | |
| </Card> | |
| ) | |
| return ( | |
| <Card className={cn("p-6 flex items-center space-x-4", className)}> | |
| <div className={cn("p-3 rounded-full flex-shrink-0", iconBgColor)}> | |
| <Icon className={cn("size-6", iconColor)} /> | |
| </div> | |
| <dl> | |
| <dd className="text-2xl font-bold text-white">{value}</dd> | |
| <dt className="text-sm text-slate-400">{label}</dt> | |
| </dl> | |
| </Card> | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/dashboard/MetricCard.tsx` around lines 24 - 34, Wrap the
metric text content in an accessible grouping so screen readers associate the
value with its label: inside the MetricCard component (the returned JSX where
{value} and {label} are rendered), replace the two sibling divs with a semantic
grouping such as a <dl> containing <dt> for the label and <dd> for the value (or
alternatively add an explicit aria-label/aria-labelledby on the wrapping div),
and ensure the class names (text-2xl font-bold text-white and text-sm
text-slate-400) are preserved on the corresponding dt/dd so styling stays the
same.
close: #38
PR: Implement Creator Dashboard Metrics and Quick Actions (#38)
Description
This PR implements the main content area for the Creator Dashboard, including reusable metric cards and a quick actions section. It also includes a fix for a common Next.js serialization error when passing complex objects between Server and Client components.
Key Changes
New Reusable Component:
MetricCard
for displaying performance data (earnings, students, etc.) with customizable icons and accent colors.
New Component:
QuickActions
containing primary dashboard triggers like "Create Course".
Dashboard Integration: Updated
src/app/dashboard/page.tsx
with a responsive grid layout.
Bug Fix: Converted the dashboard home page to a Client Component ("use client") to resolve a serialization error with Lucide icons.
Visuals
Matches Figma design with dark theme and specific color tokens.
Fully responsive (grid stacks on mobile).
Summary by CodeRabbit