Create Compenents

This commit is contained in:
AnsariTufail
2025-09-04 11:12:58 +05:30
parent 3103bb1570
commit e692b569b1
11 changed files with 611 additions and 435 deletions

100
package-lock.json generated
View File

@@ -34,6 +34,7 @@
"@radix-ui/react-toggle": "^1.1.2",
"@radix-ui/react-toggle-group": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8",
"@reduxjs/toolkit": "^2.9.0",
"@tailwindcss/postcss": "^4.1.12",
"class-variance-authority": "^0.7.1",
"clsx": "*",
@@ -48,6 +49,7 @@
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.55.0",
"react-redux": "^9.2.0",
"react-resizable-panels": "^2.1.7",
"react-router-dom": "^7.8.2",
"recharts": "^2.15.2",
@@ -1891,6 +1893,32 @@
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
"license": "MIT"
},
"node_modules/@reduxjs/toolkit": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.9.0.tgz",
"integrity": "sha512-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@standard-schema/utils": "^0.3.0",
"immer": "^10.0.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.27",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
@@ -2178,6 +2206,18 @@
"win32"
]
},
"node_modules/@standard-schema/spec": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
"license": "MIT"
},
"node_modules/@standard-schema/utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
"license": "MIT"
},
"node_modules/@swc/core": {
"version": "1.13.5",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz",
@@ -2765,6 +2805,12 @@
"@types/react": "^19.0.0"
}
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-react-swc": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.11.0.tgz",
@@ -3167,6 +3213,16 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
"node_modules/immer": {
"version": "10.1.3",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.3.tgz",
"integrity": "sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/input-otp": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz",
@@ -3691,6 +3747,29 @@
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"license": "MIT"
},
"node_modules/react-redux": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"license": "MIT",
"dependencies": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
},
"peerDependencies": {
"@types/react": "^18.2.25 || ^19",
"react": "^18.0 || ^19",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-remove-scroll": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
@@ -3871,6 +3950,27 @@
"decimal.js-light": "^2.4.1"
}
},
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT"
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"license": "MIT",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT"
},
"node_modules/rollup": {
"version": "4.49.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.49.0.tgz",

View File

@@ -29,6 +29,7 @@
"@radix-ui/react-toggle": "^1.1.2",
"@radix-ui/react-toggle-group": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8",
"@reduxjs/toolkit": "^2.9.0",
"@tailwindcss/postcss": "^4.1.12",
"class-variance-authority": "^0.7.1",
"clsx": "*",
@@ -43,6 +44,7 @@
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.55.0",
"react-redux": "^9.2.0",
"react-resizable-panels": "^2.1.7",
"react-router-dom": "^7.8.2",
"recharts": "^2.15.2",

11
src/Redux/Store.tsx Normal file
View File

@@ -0,0 +1,11 @@
import {configureStore} from '@reduxjs/toolkit';
import { demoApi } from './services/demo.service';
export const store = configureStore({
reducer: {
[demoApi.reducerPath]: demoApi.reducer},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(demoApi.middleware),
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

View File

@@ -0,0 +1,16 @@
// import { createApi } from '@reduxjs/toolkit/query'
import { fetchBaseQuery, createApi } from '@reduxjs/toolkit/query/react'
export const demoApi = createApi({
reducerPath: 'demoApi',
baseQuery: fetchBaseQuery({ baseUrl: "https://jsonplaceholder.typicode.com" }),
endpoints: (builder) => ({
// GET example
getPosts: builder.query<any[], void>({
query: () => "/posts",
}),
}),
})
export const { useGetPostsQuery } = demoApi;

View File

@@ -2,9 +2,13 @@ import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App.tsx";
import "./styles/globals.css";
import { Provider } from "react-redux";
import { store } from "./Redux/Store.tsx";
createRoot(document.getElementById("root")!).render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);

View File

@@ -40,8 +40,9 @@ import {
ExternalLink
} from 'lucide-react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts';
import exampleImage from 'figma:asset/c501c3d3f3a828828d4cb2dadb9558b43986718f.png';
// import exampleImage from 'figma:asset/c501c3d3f3a828828d4cb2dadb9558b43986718f.png';
import { useNavigate } from 'react-router-dom';
const exampleImage = 'https://via.placeholder.com/600x400?text=Example+Image';
interface SurveyProps {
userType: 'individual' | 'corporate';

View File

@@ -0,0 +1,56 @@
import React, { useEffect, useState } from "react";
import { WifiOff, Wifi, RefreshCw } from "lucide-react";
import { Alert, AlertDescription } from "../../components/ui/alert";
import { Button } from "../../components/ui/button";
// Connection status component
export function ConnectionStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
const handleRetry = async () => {
setIsLoading(true);
await new Promise(resolve => setTimeout(resolve, 1000));
setIsLoading(false);
window.location.reload();
};
if (isOnline) return null;
return (
<Alert className="mb-8 border-destructive/30 bg-gradient-to-r from-destructive/5 to-destructive/10 backdrop-blur-sm shadow-lg">
<WifiOff className="h-5 w-5 text-destructive" />
<AlertDescription className="flex items-center justify-between text-base">
<span className="font-medium">You're currently offline. Some features may not be available.</span>
<Button
onClick={handleRetry}
disabled={isLoading}
size="sm"
variant="outline"
className="ml-4 text-base min-h-[44px] border-destructive/30 text-destructive hover:bg-destructive hover:text-destructive-foreground transition-all duration-200"
>
{isLoading ? (
<RefreshCw className="h-4 w-4 animate-spin mr-2" />
) : (
<Wifi className="h-4 w-4 mr-2" />
)}
Retry
</Button>
</AlertDescription>
</Alert>
);
}

View File

@@ -0,0 +1,206 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import { Play, FastForward, CheckCircle, ChevronRight, BookOpen } from "lucide-react";
import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card";
import CurrentLearningHeaderIcon from "../../imports/CurrentLearningHeaderIcon-4285-106";
import { Badge } from "../../components/ui/badge";
import { Button } from "../../components/ui/button";
export function CurrentLearningProgress({ userType = 'individual' }: { userType?: 'individual' | 'corporate' }) {
const currentCourse = {
title: "Strategic Leadership Excellence Program",
rating: 4.9,
provider: "Dr. Rajesh Sharma, KLC",
dueDate: "Aug 30, 2025",
totalModules: 8,
currentModule: {
title: "Building High-Performance Teams",
number: 6,
progress: 65,
lessonsRemaining: 3
},
nextModule: {
title: "Leadership Communication Mastery",
number: 7
}
};
// Sample module data to match the image layout
const moduleList = [
{ title: "Leadership Fundamentals", duration: "1h 15m", completed: true },
{ title: "Emotional Intelligence for Leaders", duration: "1h 30m", completed: true },
{ title: "Strategic Decision Making", duration: "1h", completed: true },
{ title: "Conflict Resolution & Negotiation", duration: "50m", completed: true },
{ title: "Change Management Principles", duration: "40m", completed: true },
{ title: "Building High-Performance Teams", duration: "1h 20m", completed: false, current: true },
{ title: "Leadership Communication Mastery", duration: "1h 45m", completed: false, upcoming: true },
{ title: "Innovation & Strategic Thinking", duration: "2h", completed: false }
];
const completedModules = moduleList.filter(module => module.completed).length;
const totalModules = moduleList.length;
const overallProgress = Math.round((completedModules / totalModules) * 100);
const navigate = useNavigate();
return (
<Card className="h-fit bg-white shadow-md border border-gray-200 rounded-lg overflow-hidden">
<CardHeader>
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3">
<div className="p-3 bg-gradient-to-br from-brand-navy to-brand-navy/90 rounded-lg shadow-lg">
<div className="h-6 w-6">
<CurrentLearningHeaderIcon />
</div>
</div>
<CardTitle className="text-xl font-semibold text-gray-900">
Current Learning Progress
</CardTitle>
</div>
</div>
</CardHeader>
<CardContent className="px-6 pb-6">
{/* Course Header */}
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<h2 className="text-xl font-semibold text-gray-900 mb-2">
{currentCourse.title}
</h2>
<p className="text-base text-gray-600">
with {currentCourse.provider}
</p>
</div>
<div className="text-right">
<Badge className="bg-brand-navy/5 text-brand-navy border border-brand-navy px-3 py-1.5 text-sm font-medium mb-2">
In Progress
</Badge>
<p className="text-sm text-gray-600">2h 30m remaining</p>
</div>
</div>
{/* Overall Progress */}
<div className="mb-4">
<div className="flex items-center justify-between mb-3">
<span className="text-sm font-medium text-gray-700">Overall Progress</span>
<span className="text-sm font-medium text-gray-700">Modules</span>
</div>
<div className="flex items-center justify-between mb-2">
<span className="text-2xl font-bold text-gray-900">{overallProgress}%</span>
<span className="text-2xl font-bold text-gray-900">{completedModules}/{totalModules}</span>
</div>
<div className="flex items-center justify-between mb-4">
<span className="text-sm text-gray-500">Started</span>
<span className="text-sm text-gray-500">Completed</span>
</div>
{/* Progress Bar */}
<div className="w-full bg-gray-200 rounded-full h-3 mb-2">
<div
className="bg-brand-navy h-3 rounded-full transition-all duration-300"
style={{ width: `${overallProgress}%` }}
></div>
</div>
</div>
{/* Current and Up Next Modules */}
<div className="grid grid-cols-2 gap-6 mb-4">
{/* Current Module */}
<div className="bg-brand-gold/10 border border-brand-gold/30 rounded-lg p-4">
<div className="flex items-center gap-2 mb-3">
<div className="w-6 h-6 bg-brand-gold rounded flex items-center justify-center">
<Play className="h-3 w-3 text-brand-charcoal" />
</div>
<span className="text-sm font-medium text-brand-charcoal uppercase tracking-wide">Current</span>
</div>
<h3 className="text-base font-semibold text-gray-900 mb-3">
{currentCourse.currentModule.title}
</h3>
<Button
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership&module=${currentCourse.currentModule.number}`)}
className="w-full text-base min-h-[48px] bg-brand-gold hover:bg-brand-gold/90 text-brand-charcoal font-medium"
>
Continue Module
</Button>
</div>
{/* Up Next Module */}
<div className="bg-brand-navy/10 border border-brand-navy/30 rounded-lg p-4">
<div className="flex items-center gap-2 mb-3">
<div className="w-6 h-6 bg-brand-navy rounded flex items-center justify-center">
<FastForward className="h-3 w-3 text-white" />
</div>
<span className="text-sm font-medium text-brand-navy uppercase tracking-wide">Up Next</span>
</div>
<h3 className="text-base font-semibold text-gray-900 mb-3">
{currentCourse.nextModule.title}
</h3>
<Button
variant="outline"
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership&module=${currentCourse.nextModule.number}`)}
className="w-full text-base min-h-[48px] border-brand-navy text-brand-navy hover:bg-brand-navy/10 font-medium"
>
Preview Module
</Button>
</div>
</div>
{/* Course Modules List */}
<div className="mb-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-base font-semibold text-gray-900">Course Modules</h3>
<Button
variant="ghost"
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership`)}
className="text-sm text-brand-navy hover:text-brand-navy hover:bg-brand-navy/10 p-0 h-auto font-medium"
>
View All
</Button>
</div>
<div className="space-y-3">
{moduleList.slice(0, 5).map((module, index) => (
<div key={index} className="flex items-center justify-between py-2">
<div className="flex items-center gap-3">
<div className="w-5 h-5 flex items-center justify-center">
{module.completed ? (
<CheckCircle className="h-5 w-5 text-brand-gold" />
) : (
<div className="w-4 h-4 border-2 border-gray-300 rounded-full"></div>
)}
</div>
<span className={`text-sm ${module.completed ? 'text-gray-900' : 'text-gray-600'}`}>
{module.title}
</span>
</div>
<div className="flex items-center gap-2">
<span className="text-sm text-gray-500">{module.duration}</span>
<ChevronRight className="h-4 w-4 text-gray-400" />
</div>
</div>
))}
</div>
</div>
{/* Action Buttons */}
<div className="flex items-center justify-between">
<Button
variant="ghost"
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership`)}
className="text-base text-gray-600 hover:text-gray-900 hover:bg-gray-50 p-0 min-h-[48px] font-medium flex items-center gap-1"
>
<BookOpen className="h-4 w-4" />
View Full Course
</Button>
<Button
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership&module=${currentCourse.currentModule.number}`)}
className="text-base min-h-[48px] bg-brand-navy hover:bg-brand-navy/90 text-white font-medium px-6"
>
<Play className="h-4 w-4 mr-2" />
Continue Learning
</Button>
</div>
</CardContent>
</Card>
);
}

View File

@@ -78,250 +78,25 @@ import {
// import { navigate } from '../../components/Router';
import { ImageWithFallback } from '../../components/figma/ImageWithFallback';
import CurrentLearningHeaderIcon from '../../imports/CurrentLearningHeaderIcon-4285-106';
import platinumTrophy from 'figma:asset/a224fb6efc954992c535e482fe88d93f1f4178d8.png';
import goldTrophy from 'figma:asset/9ebc01e8eb24f9d71683b2ee63d224583a979590.png';
import silverTrophy from 'figma:asset/9852710543a90e291ecb85d77ea02234139264c5.png';
import bronzeTrophy from 'figma:asset/5fddc261d2e35ee810113f2537c5a59a97fd7fbd.png';
import { useNavigate } from 'react-router-dom';
import { RecentActivity } from './RecentActivity';
import { WelcomeMessage } from './WelcomeMessage';
import { CurrentLearningProgress } from './CurrentLearningProgress';
import { ConnectionStatus } from './ConnectionStatus';
const platinumTrophy = 'https://via.placeholder.com/150?text=Platinum+Trophy';
const goldTrophy = 'https://via.placeholder.com/150?text=Gold+Trophy';
const silverTrophy = 'https://via.placeholder.com/150?text=Silver+Trophy';
const bronzeTrophy = 'https://via.placeholder.com/150?text=Bronze+Trophy';
interface DashboardProps {
userType?: 'individual' | 'corporate';
}
// Recent Activity component - increased height to match Global Leaderboard
function RecentActivity() {
const activities = [
{
id: 1,
type: 'course_completed',
title: 'Completed "Strategic Decision Making"',
description: 'Module 3 of 4 • Leadership Fundamentals',
timestamp: '2 hours ago',
icon: CheckCircle,
iconColor: 'text-green-600',
iconBg: 'bg-green-100'
},
{
id: 2,
type: 'webinar_joined',
title: 'Attended Live Webinar',
description: 'Digital Transformation for Leaders',
timestamp: '5 hours ago',
icon: Video,
iconColor: 'text-blue-600',
iconBg: 'bg-blue-100'
},
{
id: 3,
type: 'assessment_completed',
title: 'Assessment Completed',
description: 'Leadership Style Assessment • Score: 92%',
timestamp: '1 day ago',
icon: FileText,
iconColor: 'text-purple-600',
iconBg: 'bg-purple-100'
},
{
id: 4,
type: 'certificate_earned',
title: 'Certificate Earned',
description: 'Leadership Foundation Certification',
timestamp: '2 days ago',
icon: Award,
iconColor: 'text-brand-gold',
iconBg: 'bg-yellow-100'
},
{
id: 5,
type: 'course_started',
title: 'Started New Course',
description: 'Innovation Leadership Track',
timestamp: '3 days ago',
icon: PlayCircle,
iconColor: 'text-green-600',
iconBg: 'bg-green-100'
},
{
id: 6,
type: 'discussion_posted',
title: 'Discussion Contribution',
description: 'Team Management Best Practices',
timestamp: '4 days ago',
icon: MessageSquare,
iconColor: 'text-orange-600',
iconBg: 'bg-orange-100'
},
{
id: 7,
type: 'resource_downloaded',
title: 'Resource Downloaded',
description: 'Leadership Skills Handbook PDF',
timestamp: '5 days ago',
icon: BookOpen,
iconColor: 'text-gray-600',
iconBg: 'bg-gray-100'
},
{
id: 8,
type: 'peer_connection',
title: 'New Connection',
description: 'Connected with 3 peer learners',
timestamp: '1 week ago',
icon: Users,
iconColor: 'text-brand-navy',
iconBg: 'bg-blue-100'
}
];
return (
<Card className="h-96 flex flex-col border-0 shadow-lg bg-white">
<CardHeader className="pb-3 flex-shrink-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-gradient-to-br from-brand-navy to-brand-navy/90 rounded-lg flex items-center justify-center">
<Activity className="h-4 w-4 text-white" />
</div>
<CardTitle className="text-lg font-semibold text-gray-900">
Recent Activity
</CardTitle>
</div>
<Button
variant="outline"
size="sm"
onClick={() => navigate('/dashboard?view=individual&tab=activity')}
className="text-brand-navy hover:bg-brand-navy/10 border-brand-navy/20 text-base px-3 py-1.5 min-h-[48px] font-medium"
>
View All
</Button>
</div>
<p className="text-sm text-gray-600 mt-1">
Your recent learning activities
</p>
</CardHeader>
<CardContent className="px-6 pb-4 flex-1 min-h-0">
<ScrollArea className="h-full">
<div className="space-y-2 pr-2">
{activities.map((activity) => {
const Icon = activity.icon;
return (
<div
key={activity.id}
className="flex items-start gap-3 p-3 rounded-lg bg-gray-50 hover:bg-gray-100 transition-all duration-200"
>
{/* Activity Icon */}
<div className={`p-2 rounded-lg ${activity.iconBg} flex-shrink-0`}>
<Icon className={`h-3 w-3 ${activity.iconColor}`} />
</div>
{/* Activity Content */}
<div className="flex-1 min-w-0">
<h4 className="text-sm font-semibold text-gray-900 mb-1 leading-tight">
{activity.title}
</h4>
<p className="text-xs text-gray-600 mb-1 leading-relaxed">
{activity.description}
</p>
<p className="text-xs text-gray-500 font-medium">
{activity.timestamp}
</p>
</div>
</div>
);
})}
</div>
</ScrollArea>
</CardContent>
</Card>
);
}
// Welcome Message component with simple visible emoji
function WelcomeMessage({ user }: { user: any }) {
const [showEmoji, setShowEmoji] = useState(true);
const emojiChar = '👋';
useEffect(() => {
const timer = setTimeout(() => {
setShowEmoji(false);
}, 5000);
return () => clearTimeout(timer);
}, []);
return (
<div className="text-4xl font-bold mb-3">
<span className="bg-gradient-to-r from-gray-800 via-gray-700 to-gray-600 bg-clip-text text-transparent">
Welcome back, Priya Sharma!
</span>
{showEmoji && (
<span
className="text-yellow-500 ml-2 inline-block animate-wave-emoji"
style={{
fontSize: '2.5rem',
fontFamily: 'Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji, sans-serif',
backgroundColor: 'transparent',
color: '#EAB308'
}}
>
{emojiChar}
</span>
)}
</div>
);
}
// Connection status component
function ConnectionStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
const handleRetry = async () => {
setIsLoading(true);
await new Promise(resolve => setTimeout(resolve, 1000));
setIsLoading(false);
window.location.reload();
};
if (isOnline) return null;
return (
<Alert className="mb-8 border-destructive/30 bg-gradient-to-r from-destructive/5 to-destructive/10 backdrop-blur-sm shadow-lg">
<WifiOff className="h-5 w-5 text-destructive" />
<AlertDescription className="flex items-center justify-between text-base">
<span className="font-medium">You're currently offline. Some features may not be available.</span>
<Button
onClick={handleRetry}
disabled={isLoading}
size="sm"
variant="outline"
className="ml-4 text-base min-h-[44px] border-destructive/30 text-destructive hover:bg-destructive hover:text-destructive-foreground transition-all duration-200"
>
{isLoading ? (
<RefreshCw className="h-4 w-4 animate-spin mr-2" />
) : (
<Wifi className="h-4 w-4 mr-2" />
)}
Retry
</Button>
</AlertDescription>
</Alert>
);
}
// Weekly Progress Goal Setting Modal component
function WeeklyProgressModal({
@@ -698,205 +473,6 @@ function AchievementBadges() {
);
}
// Current Learning Progress component
function CurrentLearningProgress({ userType = 'individual' }: { userType?: 'individual' | 'corporate' }) {
const currentCourse = {
title: "Strategic Leadership Excellence Program",
rating: 4.9,
provider: "Dr. Rajesh Sharma, KLC",
dueDate: "Aug 30, 2025",
totalModules: 8,
currentModule: {
title: "Building High-Performance Teams",
number: 6,
progress: 65,
lessonsRemaining: 3
},
nextModule: {
title: "Leadership Communication Mastery",
number: 7
}
};
// Sample module data to match the image layout
const moduleList = [
{ title: "Leadership Fundamentals", duration: "1h 15m", completed: true },
{ title: "Emotional Intelligence for Leaders", duration: "1h 30m", completed: true },
{ title: "Strategic Decision Making", duration: "1h", completed: true },
{ title: "Conflict Resolution & Negotiation", duration: "50m", completed: true },
{ title: "Change Management Principles", duration: "40m", completed: true },
{ title: "Building High-Performance Teams", duration: "1h 20m", completed: false, current: true },
{ title: "Leadership Communication Mastery", duration: "1h 45m", completed: false, upcoming: true },
{ title: "Innovation & Strategic Thinking", duration: "2h", completed: false }
];
const completedModules = moduleList.filter(module => module.completed).length;
const totalModules = moduleList.length;
const overallProgress = Math.round((completedModules / totalModules) * 100);
return (
<Card className="h-fit bg-white shadow-md border border-gray-200 rounded-lg overflow-hidden">
<CardHeader>
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3">
<div className="p-3 bg-gradient-to-br from-brand-navy to-brand-navy/90 rounded-lg shadow-lg">
<div className="h-6 w-6">
<CurrentLearningHeaderIcon />
</div>
</div>
<CardTitle className="text-xl font-semibold text-gray-900">
Current Learning Progress
</CardTitle>
</div>
</div>
</CardHeader>
<CardContent className="px-6 pb-6">
{/* Course Header */}
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<h2 className="text-xl font-semibold text-gray-900 mb-2">
{currentCourse.title}
</h2>
<p className="text-base text-gray-600">
with {currentCourse.provider}
</p>
</div>
<div className="text-right">
<Badge className="bg-brand-navy/5 text-brand-navy border border-brand-navy px-3 py-1.5 text-sm font-medium mb-2">
In Progress
</Badge>
<p className="text-sm text-gray-600">2h 30m remaining</p>
</div>
</div>
{/* Overall Progress */}
<div className="mb-4">
<div className="flex items-center justify-between mb-3">
<span className="text-sm font-medium text-gray-700">Overall Progress</span>
<span className="text-sm font-medium text-gray-700">Modules</span>
</div>
<div className="flex items-center justify-between mb-2">
<span className="text-2xl font-bold text-gray-900">{overallProgress}%</span>
<span className="text-2xl font-bold text-gray-900">{completedModules}/{totalModules}</span>
</div>
<div className="flex items-center justify-between mb-4">
<span className="text-sm text-gray-500">Started</span>
<span className="text-sm text-gray-500">Completed</span>
</div>
{/* Progress Bar */}
<div className="w-full bg-gray-200 rounded-full h-3 mb-2">
<div
className="bg-brand-navy h-3 rounded-full transition-all duration-300"
style={{ width: `${overallProgress}%` }}
></div>
</div>
</div>
{/* Current and Up Next Modules */}
<div className="grid grid-cols-2 gap-6 mb-4">
{/* Current Module */}
<div className="bg-brand-gold/10 border border-brand-gold/30 rounded-lg p-4">
<div className="flex items-center gap-2 mb-3">
<div className="w-6 h-6 bg-brand-gold rounded flex items-center justify-center">
<Play className="h-3 w-3 text-brand-charcoal" />
</div>
<span className="text-sm font-medium text-brand-charcoal uppercase tracking-wide">Current</span>
</div>
<h3 className="text-base font-semibold text-gray-900 mb-3">
{currentCourse.currentModule.title}
</h3>
<Button
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership&module=${currentCourse.currentModule.number}`)}
className="w-full text-base min-h-[48px] bg-brand-gold hover:bg-brand-gold/90 text-brand-charcoal font-medium"
>
Continue Module →
</Button>
</div>
{/* Up Next Module */}
<div className="bg-brand-navy/10 border border-brand-navy/30 rounded-lg p-4">
<div className="flex items-center gap-2 mb-3">
<div className="w-6 h-6 bg-brand-navy rounded flex items-center justify-center">
<FastForward className="h-3 w-3 text-white" />
</div>
<span className="text-sm font-medium text-brand-navy uppercase tracking-wide">Up Next</span>
</div>
<h3 className="text-base font-semibold text-gray-900 mb-3">
{currentCourse.nextModule.title}
</h3>
<Button
variant="outline"
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership&module=${currentCourse.nextModule.number}`)}
className="w-full text-base min-h-[48px] border-brand-navy text-brand-navy hover:bg-brand-navy/10 font-medium"
>
Preview Module →
</Button>
</div>
</div>
{/* Course Modules List */}
<div className="mb-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-base font-semibold text-gray-900">Course Modules</h3>
<Button
variant="ghost"
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership`)}
className="text-sm text-brand-navy hover:text-brand-navy hover:bg-brand-navy/10 p-0 h-auto font-medium"
>
View All →
</Button>
</div>
<div className="space-y-3">
{moduleList.slice(0, 5).map((module, index) => (
<div key={index} className="flex items-center justify-between py-2">
<div className="flex items-center gap-3">
<div className="w-5 h-5 flex items-center justify-center">
{module.completed ? (
<CheckCircle className="h-5 w-5 text-brand-gold" />
) : (
<div className="w-4 h-4 border-2 border-gray-300 rounded-full"></div>
)}
</div>
<span className={`text-sm ${module.completed ? 'text-gray-900' : 'text-gray-600'}`}>
{module.title}
</span>
</div>
<div className="flex items-center gap-2">
<span className="text-sm text-gray-500">{module.duration}</span>
<ChevronRight className="h-4 w-4 text-gray-400" />
</div>
</div>
))}
</div>
</div>
{/* Action Buttons */}
<div className="flex items-center justify-between">
<Button
variant="ghost"
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership`)}
className="text-base text-gray-600 hover:text-gray-900 hover:bg-gray-50 p-0 min-h-[48px] font-medium flex items-center gap-1"
>
<BookOpen className="h-4 w-4" />
View Full Course
</Button>
<Button
onClick={() => navigate(`/course?view=${userType}&courseId=strategic-leadership&module=${currentCourse.currentModule.number}`)}
className="text-base min-h-[48px] bg-brand-navy hover:bg-brand-navy/90 text-white font-medium px-6"
>
<Play className="h-4 w-4 mr-2" />
Continue Learning
</Button>
</div>
</CardContent>
</Card>
);
}
// Global Leaderboard component - increased height to match Recent Activity
function GlobalLeaderboard() {
const leaderboardData = [
@@ -976,6 +552,7 @@ function GlobalLeaderboard() {
default: return 'bg-gray-100 text-gray-600';
}
};
const navigate = useNavigate();
return (
<Card className="h-96 flex flex-col border-0 shadow-lg bg-white">
@@ -1443,9 +1020,9 @@ export default function Dashboard({ userType = 'individual' }: DashboardProps) {
const user = { name: 'Alex', email: 'alex@example.com' };
return (
<LearnerLayout currentPage="/dashboard" userType={userType}>
<LearnerLayout currentPage="/dashboard" userType={userType}>
<div className="container mx-auto px-4 lg:px-8 py-8 space-y-8">
<ConnectionStatus />
<ConnectionStatus />
{/* Welcome Section */}
<div className="mb-8">

View File

@@ -0,0 +1,167 @@
import {
CheckCircle,
Video,
FileText,
Award,
PlayCircle,
MessageSquare,
BookOpen,
Users,
Activity
} from "lucide-react";
import { useNavigate } from "react-router-dom";
import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card";
import { Button } from "../../components/ui/button";
import { ScrollArea } from "../../components/ui/scroll-area";
// import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
// import { Button } from "@/components/ui/button";
// import { ScrollArea } from "@/components/ui/scroll-area";
// Recent Activity component - increased height to match Global Leaderboard
export function RecentActivity() {
const activities = [
{
id: 1,
type: 'course_completed',
title: 'Completed "Strategic Decision Making"',
description: 'Module 3 of 4 • Leadership Fundamentals',
timestamp: '2 hours ago',
icon: CheckCircle,
iconColor: 'text-green-600',
iconBg: 'bg-green-100'
},
{
id: 2,
type: 'webinar_joined',
title: 'Attended Live Webinar',
description: 'Digital Transformation for Leaders',
timestamp: '5 hours ago',
icon: Video,
iconColor: 'text-blue-600',
iconBg: 'bg-blue-100'
},
{
id: 3,
type: 'assessment_completed',
title: 'Assessment Completed',
description: 'Leadership Style Assessment • Score: 92%',
timestamp: '1 day ago',
icon: FileText,
iconColor: 'text-purple-600',
iconBg: 'bg-purple-100'
},
{
id: 4,
type: 'certificate_earned',
title: 'Certificate Earned',
description: 'Leadership Foundation Certification',
timestamp: '2 days ago',
icon: Award,
iconColor: 'text-brand-gold',
iconBg: 'bg-yellow-100'
},
{
id: 5,
type: 'course_started',
title: 'Started New Course',
description: 'Innovation Leadership Track',
timestamp: '3 days ago',
icon: PlayCircle,
iconColor: 'text-green-600',
iconBg: 'bg-green-100'
},
{
id: 6,
type: 'discussion_posted',
title: 'Discussion Contribution',
description: 'Team Management Best Practices',
timestamp: '4 days ago',
icon: MessageSquare,
iconColor: 'text-orange-600',
iconBg: 'bg-orange-100'
},
{
id: 7,
type: 'resource_downloaded',
title: 'Resource Downloaded',
description: 'Leadership Skills Handbook PDF',
timestamp: '5 days ago',
icon: BookOpen,
iconColor: 'text-gray-600',
iconBg: 'bg-gray-100'
},
{
id: 8,
type: 'peer_connection',
title: 'New Connection',
description: 'Connected with 3 peer learners',
timestamp: '1 week ago',
icon: Users,
iconColor: 'text-brand-navy',
iconBg: 'bg-blue-100'
}
];
const navigate = useNavigate();
return (
<Card className="h-96 flex flex-col border-0 shadow-lg bg-white">
<CardHeader className="pb-3 flex-shrink-0">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-gradient-to-br from-brand-navy to-brand-navy/90 rounded-lg flex items-center justify-center">
<Activity className="h-4 w-4 text-white" />
</div>
<CardTitle className="text-lg font-semibold text-gray-900">
Recent Activity
</CardTitle>
</div>
<Button
variant="outline"
size="sm"
onClick={() => navigate('/dashboard?view=individual&tab=activity')}
className="text-brand-navy hover:bg-brand-navy/10 border-brand-navy/20 text-base px-3 py-1.5 min-h-[48px] font-medium"
>
View All
</Button>
</div>
<p className="text-sm text-gray-600 mt-1">
Your recent learning activities
</p>
</CardHeader>
<CardContent className="px-6 pb-4 flex-1 min-h-0">
<ScrollArea className="h-full">
<div className="space-y-2 pr-2">
{activities.map((activity) => {
const Icon = activity.icon;
return (
<div
key={activity.id}
className="flex items-start gap-3 p-3 rounded-lg bg-gray-50 hover:bg-gray-100 transition-all duration-200"
>
{/* Activity Icon */}
<div className={`p-2 rounded-lg ${activity.iconBg} flex-shrink-0`}>
<Icon className={`h-3 w-3 ${activity.iconColor}`} />
</div>
{/* Activity Content */}
<div className="flex-1 min-w-0">
<h4 className="text-sm font-semibold text-gray-900 mb-1 leading-tight">
{activity.title}
</h4>
<p className="text-xs text-gray-600 mb-1 leading-relaxed">
{activity.description}
</p>
<p className="text-xs text-gray-500 font-medium">
{activity.timestamp}
</p>
</div>
</div>
);
})}
</div>
</ScrollArea>
</CardContent>
</Card>
);
}

View File

@@ -0,0 +1,36 @@
import React, { useState, useEffect } from "react";
// Welcome Message component with simple visible emoji
export function WelcomeMessage({ user }: { user: any }) {
const [showEmoji, setShowEmoji] = useState(true);
const emojiChar = '👋';
useEffect(() => {
const timer = setTimeout(() => {
setShowEmoji(false);
}, 5000);
return () => clearTimeout(timer);
}, []);
return (
<div className="text-4xl font-bold mb-3">
<span className="bg-gradient-to-r from-gray-800 via-gray-700 to-gray-600 bg-clip-text text-transparent">
Welcome back, Priya Sharma!
</span>
{showEmoji && (
<span
className="text-yellow-500 ml-2 inline-block animate-wave-emoji"
style={{
fontSize: '2.5rem',
fontFamily: 'Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji, sans-serif',
backgroundColor: 'transparent',
color: '#EAB308'
}}
>
{emojiChar}
</span>
)}
</div>
);
}