Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/components/layout/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Users,
UserCheck,
Award,
LayoutDashboard,
} from "lucide-react";
import { useState } from "react";
import { NavLink, useNavigate } from "react-router-dom";
Expand Down Expand Up @@ -180,6 +181,22 @@ const Sidebar = ({ isOpen, onClose }) => {
)}
</div>

{/* Dashboard */}
<NavLink to="dashboard">
<button
onClick={() => handleNavClick("dashboard")}
className="flex items-center gap-3 px-3 py-2 w-full text-left rounded-lg transition-colors hover:bg-[#374151]"
style={{
backgroundColor:
currentPage === "dashboard" ? "#1f2937" : "transparent",
color: "#FFFFFF",
}}
>
<LayoutDashboard className="w-5 h-5 text-[#ced2d8]" />
<span className="text-sm font-medium">Dashboard</span>
</button>
</NavLink>

{/* Courses */}
<NavLink to="course-listing">
<button
Expand Down
157 changes: 157 additions & 0 deletions src/data/mock-dashboard-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
export const dashboardData = {
"gamification": {
"pointsTotal": 1320,
"streakCount": 4,
"overallCompletion": 26,
"lastActiveAt": "2025-08-11T09:30:00Z",
"badges": ["Streak Starter", "Grammar Guru", "Pronunciation Explorer"]
},

"lessons": [
{
"areaId": "basic-conv",
"lessonId": "greetings-1",
"title": "Greetings & Introductions",
"status": "Continue",
"completed": true,
"score": 92,
"lastAttemptAt": "2025-08-11T09:30:00Z",
"percentComplete": 100,
"nextLesson": {
"lessonId": "basic-2",
"title": "Greetings & Introductions 2",
},
},
{
"areaId": "grammar-found",
"lessonId": "present-simple-1",
"title": "Grammar Foundations",
"status": "Continue",
"completed": true,
"score": 78,
"lastAttemptAt": "2025-08-10T15:00:00Z",
"percentComplete": 100,
"nextLesson": {
"lessonId": "grammar-2",
"title": "Present Simple vs Continuous",
},
},
{
"areaId": "cultural-insights",
"lessonId": "holidays-1",
"title": "American vs British English",
"status": "Continue",
"completed": true,
"score": 66,
"lastAttemptAt": "2025-08-09T14:20:00Z",
"percentComplete": 100,
"nextLesson": {
"lessonId": "cultural-2",
"title": "Holidays & Traditions 2",
},
},
{
"areaId": "pronunciation",
"lessonId": "th-sound-1",
"title": "Difficult TH Sound",
"status": "Start",
"completed": false,
"lastAttemptAt": "2025-08-08T17:45:00Z",
"percentComplete": 0,
"nextLesson": {
"lessonId": "th-sound-2",
"title": "Difficult TH Sound 2",
},
}
],

"recommendations": [
{
"title": "Pronunciation: TH Sound",
"tag": "New",
"description": "Target Spanish speaker challenges with /Γ°/ and /ΞΈ/...",
"rating": 5,
"action": "Practice now"
},
{
"title": "Present Simple Sprint",
"tag": "Hot",
"description": "Quick drills to perfect third-person forms and adverb placement.",
"rating": 5,
"action": "Start sprint"
}
],

"progress": {
"perLesson": [
{ "areaId": "basic-conv", "lessonId": "greetings-1", "completed": true, "score": 92, "lastAttemptAt": "2025-08-11T09:30:00Z" },
{ "areaId": "grammar-found", "lessonId": "present-simple-1", "completed": true, "score": 78, "lastAttemptAt": "2025-08-10T15:00:00Z" },
{ "areaId": "cultural-insights", "lessonId": "holidays-1", "completed": true, "score": 66, "lastAttemptAt": "2025-08-09T14:20:00Z" },
{ "areaId": "pronunciation", "lessonId": "th-sound-1", "completed": false, "lastAttemptAt": "2025-08-08T17:45:00Z" }
],
"areaSummary": [
{ "areaId": "basic-conv", "percentComplete": 56, "lastLessonId": "greetings-1" },
{ "areaId": "grammar-found", "percentComplete": 34, "lastLessonId": "present-simple-1" },
{ "areaId": "cultural-insights", "percentComplete": 12, "lastLessonId": "holidays-1" },
{ "areaId": "pronunciation", "percentComplete": 0 }
]
},

"activity": [
{ "id": "a1", "type": "lesson", "areaId": "basic-conv", "title": "Asking for Directions", "score": 92, "date": "2025-08-11T09:30:00Z" },
{ "id": "a2", "type": "assessment", "areaId": "grammar-found", "title": "Present Simple Check", "score": 78, "date": "2025-08-10T15:00:00Z" },
{ "id": "a3", "type": "lesson", "areaId": "cultural-insights", "title": "Holidays & Traditions", "score": 66, "date": "2025-08-09T14:20:00Z" },
{ "id": "a4", "type": "assessment", "areaId": "pronunciation", "title": "TH vs T Minimal Pairs", "score": 54, "date": "2025-08-08T17:45:00Z" },
{ "id": "a5", "type": "lesson", "areaId": "grammar-found", "title": "Question Formation", "score": 88, "date": "2025-08-07T11:10:00Z" }
],

"recentActivity": [
{
"title": "Asking for Directions",
"type": "Lesson - Basic Conversation",
"date": "11/8/2025",
"score": 92
},
{
"title": "Present Simple Check",
"type": "Assessment - Grammar Foundations",
"date": "10/8/2025",
"score": 78
},
{
"title": "Holidays & Traditions",
"type": "Lesson - Cultural Insights",
"date": "9/8/2025",
"score": 66
},
{
"title": "TH vs T Minimal Pairs",
"type": "Assessment - Pronunciation & Listening",
"date": "8/8/2025",
"score": 54
},
{
"title": "Question Formation",
"type": "Lesson - Grammar Foundations",
"date": "7/8/2025",
"score": 88
}
]
};

// Helper function to get dashboard data
export const getDashboardData = () => {
return dashboardData;
};

// Helper function to get the next lesson to continue learning
export const getContinueLearningLesson = () => {
const { lessons } = dashboardData;

// First incomplete lesson
const nextLesson = lessons.find(lesson => !lesson.completed);
if (nextLesson) return nextLesson;

// If all are completed, return the last one (most recent)
return lessons[lessons.length - 1] || null;
};
186 changes: 186 additions & 0 deletions src/pages/aurora-site/dashboard/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { getContinueLearningLesson, getDashboardData } from "@/data/mock-dashboard-data";
import { CircleCheck, Star } from "lucide-react";
import React from "react";

const TopCard = ({ title, description, stat }) => {
return (
<Card className="w-full">
<div className="w-full h-2 bg-gradient-to-r from-blue-600 to-cyan-400 rounded-t-lg"></div>
<CardContent className="p-4 flex flex-col gap-2">
<div className="flex justify-between items-center">
<p className="text-sm text-gray-500 font-medium">{title}</p>
<div></div>
</div>
<p className="text-2xl font-bold">{stat}</p>
<div>
<p className="text-[12px] text-gray-500">{description}</p>
</div>
</CardContent>
</Card>
);
};

const Dashboard = () => {
const dashboardData = getDashboardData();
const continueLearningLesson = getContinueLearningLesson();

return (
<div className="min-h-screen bg-dark-blue-6">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header Section */}
<div className="mb-8">
<div className="flex items-center gap-3 mb-4">
<div>
<h1 className="text-3xl font-bold text-white">Dashboard</h1>
<p className="text-neutral-3 mt-1">
Your learning overview and next steps
</p>
</div>
</div>
</div>

{/* Top cards Section */}
<div className="flex flex-col md:flex-row gap-4 items-center w-full mb-5">
<TopCard
title="Total Points"
description="Earn points by completing lessons and assessments"
stat={dashboardData.gamification.pointsTotal}
/>
<TopCard
title="Streak"
description="Come back every day to grow your streak"
stat={dashboardData.gamification.streakCount}
/>
<TopCard
title="Overall Completion"
description="Average across all areas"
stat={`${dashboardData.gamification.overallCompletion}%`}
/>
</div>

<div className="w-full flex flex-col lg:flex-row gap-4">
<div className="w-full lg:w-[70%] space-y-5">
<Card className="flex pt-6">
<CardContent className="w-full flex justify-between items-center">
<div className="flex flex-col gap-2">
<p className="text-sm text-gray-500 font-medium">
Continue where you left off
</p>
<p className="font-medium">{`${continueLearningLesson.title}`}</p>
</div>
<Button>Continue</Button>
</CardContent>
</Card>
<div className="space-y-3">
<div className="flex justify-between items-center">
<h2 className="text-xl font-bold text-white">Your Courses</h2>
<a>Explore All</a>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{dashboardData?.lessons.map(
({ title, percentComplete, nextLesson, status }) => (
<Card>
<img
src="https://placehold.co/400"
className="w-full h-44 object-cover rounded-t-lg"
/>
<CardContent className="pt-4 flex flex-col gap-2">
<p className="font-semibold">{title}</p>
<div className="flex flex-col">
<div className="flex justify-between items-center">
<p className="text-sm text-gray-500">Progress</p>
<p className="text-sm font-semibold">{percentComplete}%</p>
</div>
<div>line</div>
</div>
<div className="flex justify-between items-center">
<p className="text-gray-500 text-sm">
Next:{" "}
<span className="text-gray-800 font-medium">
{nextLesson.title}
</span>
</p>
<Button>{status}</Button>
</div>
</CardContent>
</Card>
)
)}
</div>
</div>
</div>
<div className="w-full lg:w-[30%] flex flex-col md:flex-row lg:flex-col gap-3">
<Card className="w-full">
<CardContent className="pt-4">
<h2 className="text-xl font-bold mb-3">Recommended for you</h2>
<div className="flex flex-col gap-3">
{dashboardData?.recommendations.map(
({ title, description, tag }) => (
<div className="flex gap-2">
<div className="w-16 h-12 rounded-md bg-gray-200">
{/* <img className="w-16 h-16 rounded-md"/> */}
</div>
<div className="flex flex-col gap-2">
<div className="flex justify-between items-center">
<div>
<p className="font-semibold">{title}</p>
<div className="bg-blue-200 rounded-lg text-[10px] font-semibold px-1 py-[1px] w-fit">
{tag}
</div>
</div>
<Button>Start Learning</Button>
</div>
<div>
<p className="text-gray-500 text-sm">
{description}
</p>
<div className="flex flex-col">
<div>
<Star className="w-4 h-4 text-blue-500" />
</div>
<div>line</div>
</div>
</div>
</div>
</div>
)
)}
</div>
</CardContent>
</Card>
<Card className="w-full">
<CardContent className="pt-4">
<h2 className="text-xl font-bold mb-3">Recent Activity</h2>
<div className="flex flex-col">
{dashboardData?.recentActivity.map(
({ title, type, date, score }) => (
<div className="flex flex-col">
<div className="flex gap-2 w-full justify-between">
<div className="flex gap-2 items-start">
<CircleCheck className="w-4 h-4 mt-1 text-blue-500" />
<div>
<p className="text-sm font-semibold">{title}</p>
<div className="text-sm text-gray-500">
{type}
</div>
</div>
</div>
<p className="text-sm font-bold">{score}%</p>
</div>
<hr className="my-4" />
</div>
)
)}
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
);
};

export default Dashboard;