I'll create a plan for you to review and approve.
Here are the important artifacts that you might want to review before I start generating the code. You can review, edit and save (ctrl+s) them.
// Props types (data passed to components)
export interface YourCourse {
id: string;
title: string;
icon: string;
completionPercentage: number;
timeLeft: number;
progressColor: string;
}
export interface AllCourse {
id: string;
title: string;
description: string;
image: string;
difficulty: string;
duration: number;
rating: number;
enrollmentCount: number;
difficultyColor: string;
}
export interface User {
name: string;
role: string;
avatar: string;
}
export interface CoursesDashboardProps {
user: User;
yourCourses: YourCourse[];
allCourses: AllCourse[];
}
// No API endpoints required for this static dashboard component
// Props for the root CoursesDashboard component
export interface CoursesDashboardRootProps {
user: User;
yourCourses: YourCourse[];
allCourses: AllCourse[];
}
// Update theme to include course-specific colors and progress bar styling
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
colorSchemes: {
light: {
palette: {
primary: {
main: '#1a4e44',
},
secondary: {
main: '#808897',
},
text: {
primary: '#0d0d12',
secondary: '#666d80',
},
background: {
default: '#ffffff',
paper: '#f8f9fb',
},
divider: '#dfe1e6',
grey: {
50: '#f8f9fb',
100: '#eeeff2',
200: '#dfe1e6',
300: '#a4abb8',
400: '#808897',
500: '#666d80',
},
success: {
main: '#2c8f88',
},
warning: {
main: '#ffbd4c',
}
}
},
dark: {
palette: {
primary: {
main: '#ffffff',
},
secondary: {
main: '#9ca3af',
},
text: {
primary: '#ffffff',
secondary: '#9ca3af',
},
background: {
default: '#111827',
paper: '#1f2937',
},
divider: '#374151',
grey: {
50: '#1f2937',
100: '#374151',
200: '#4b5563',
300: '#6b7280',
400: '#9ca3af',
500: '#d1d5db',
},
success: {
main: '#2c8f88',
},
warning: {
main: '#ffbd4c',
}
}
}
},
typography: {
fontFamily: "'Inter Tight', sans-serif",
h4: {
fontSize: '24px',
fontWeight: 600,
},
h5: {
fontSize: '20px',
fontWeight: 600,
},
h6: {
fontSize: '16px',
fontWeight: 600,
lineHeight: '24px',
letterSpacing: '0.32px',
},
body1: {
fontSize: '16px',
fontWeight: 500,
letterSpacing: '0.32px',
},
body2: {
fontSize: '14px',
fontWeight: 500,
letterSpacing: '0.28px',
},
caption: {
fontSize: '12px',
fontWeight: 400,
letterSpacing: '0.24px',
},
},
shape: {
borderRadius: 8,
},
});
// Course difficulty levels and completion status
export enum CourseDifficulty {
BEGINNER = 'Beginner',
INTERMEDIATE = 'Intermediate',
ADVANCED = 'Advanced'
}
export enum CourseStatus {
IN_PROGRESS = 'in_progress',
COMPLETED = 'completed',
NOT_STARTED = 'not_started'
}
Now I'll generate the code files for the courses dashboard with progress cards, course grid layout, filters and sorting functionality.
// Course difficulty levels and completion status
export enum CourseDifficulty {
BEGINNER = 'Beginner',
INTERMEDIATE = 'Intermediate',
ADVANCED = 'Advanced'
}
export enum CourseStatus {
IN_PROGRESS = 'in_progress',
COMPLETED = 'completed',
NOT_STARTED = 'not_started'
}
export const formatCompletionPercentage = (percentage: number): string => {
return `${percentage}% complete`;
};
export const formatTimeLeft = (hours: number): string => {
if (hours === 0) return '0 hours left';
if (hours < 1) return `${hours} hours left`;
return `${Math.floor(hours)} hours left`;
};
export const formatRating = (rating: number): string => {
return rating.toFixed(1);
};
export const formatEnrollmentCount = (count: number): string => {
return `(${count.toLocaleString()})`;
};
export const formatDuration = (hours: number): string => {
return `${hours} hours`;
};
// Props types (data passed to components)
export interface YourCourse {
id: string;
title: string;
icon: string;
completionPercentage: number;
timeLeft: number;
progressColor: string;
}
export interface AllCourse {
id: string;
title: string;
description: string;
image: string;
difficulty: string;
duration: number;
rating: number;
enrollmentCount: number;
difficultyColor: string;
}
export interface User {
name: string;
role: string;
avatar: string;
}
export interface CoursesDashboardProps {
user: User;
yourCourses: YourCourse[];
allCourses: AllCourse[];
}
// Data for the courses dashboard
export const mockRootProps = {
user: {
name: "Alesia K.",
role: "Pro Member",
avatar: "https://i.pravatar.cc/150?img=1"
},
yourCourses: [
{
id: "1",
title: "Design Accessibility",
icon: "/images/course-icon-1.png",
completionPercentage: 25,
timeLeft: 3,
progressColor: "#2c8f88"
},
{
id: "2",
title: "UX Research",
icon: "/images/course-icon-2.png",
completionPercentage: 70,
timeLeft: 1.5,
progressColor: "#2c8f88"
},
{
id: "3",
title: "Design Terminology",
icon: "/images/course-icon-3.png",
completionPercentage: 100,
timeLeft: 0,
progressColor: "#2c8f88"
}
],
allCourses: [
{
id: "4",
title: "Design Workshop Facilitation",
description: "Master your skills in design workshop facilitation and learn how to promote collaboration and find the best design solutions. Learn what activities, tools, and deliverables can enhance your workshops.",
image: "/images/course-image-1.png",
difficulty: "Advanced",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#2c8f88"
},
{
id: "5",
title: "Animation for Beginner",
description: "Procreate Dreams has transformed my ability to make animations from my art. Yet when I first opened the app, I was so frustrated: I didn't understand what I could actually do and was stumped when it didn't behave like I thought it would. Cut to today, I've fallen in love with animation as an art form and am blown away with how easily I can create something cool. Even beginners can create impressive results.",
image: "/images/course-image-2.png",
difficulty: "Beginner",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#8b5cf6"
},
{
id: "6",
title: "Common Design Pattern",
description: "Master your skills in design workshop facilitation and learn how to promote collaboration and find the best design solutions. Learn what activities, tools, and deliverables can enhance your workshops.",
image: "/images/course-image-3.png",
difficulty: "Intermediate",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#3b82f6"
},
{
id: "7",
title: "3D Design Foundation",
description: "Learn about the many applications of 3D design and get familiar with the basics of 3D, such as the XYZ coordinate system, 3D shape primitives, perception, lighting, and materials.",
image: "/images/course-image-4.png",
difficulty: "Advanced",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#8b5cf6"
},
{
id: "8",
title: "Framer for Beginner",
description: "Master your skills in design workshop facilitation and learn how to promote collaboration and find the best design solutions. Learn what activities, tools, and deliverables can enhance your workshops.",
image: "/images/course-image-5.png",
difficulty: "Beginner",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#1f2937"
},
{
id: "9",
title: "Design Workshop Facilitation",
description: "Master your skills in design workshop facilitation and learn how to promote collaboration and find the best design solutions. Learn what activities, tools, and deliverables can enhance your workshops.",
image: "/images/course-image-6.png",
difficulty: "Advanced",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#f97316"
}
]
};
Now I'll generate the code files for the courses dashboard with progress cards, course grid layout, filters and sorting functionality.
// Update theme to include course-specific colors and progress bar styling
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
colorSchemes: {
light: {
palette: {
primary: {
main: '#1a4e44',
},
secondary: {
main: '#808897',
},
text: {
primary: '#0d0d12',
secondary: '#666d80',
},
background: {
default: '#ffffff',
paper: '#f8f9fb',
},
divider: '#dfe1e6',
grey: {
50: '#f8f9fb',
100: '#eeeff2',
200: '#dfe1e6',
300: '#a4abb8',
400: '#808897',
500: '#666d80',
},
success: {
main: '#2c8f88',
},
warning: {
main: '#ffbd4c',
}
}
},
dark: {
palette: {
primary: {
main: '#ffffff',
},
secondary: {
main: '#9ca3af',
},
text: {
primary: '#ffffff',
secondary: '#9ca3af',
},
background: {
default: '#111827',
paper: '#1f2937',
},
divider: '#374151',
grey: {
50: '#1f2937',
100: '#374151',
200: '#4b5563',
300: '#6b7280',
400: '#9ca3af',
500: '#d1d5db',
},
success: {
main: '#2c8f88',
},
warning: {
main: '#ffbd4c',
}
}
}
},
typography: {
fontFamily: "'Inter Tight', sans-serif",
h4: {
fontSize: '24px',
fontWeight: 600,
},
h5: {
fontSize: '20px',
fontWeight: 600,
},
h6: {
fontSize: '16px',
fontWeight: 600,
lineHeight: '24px',
letterSpacing: '0.32px',
},
body1: {
fontSize: '16px',
fontWeight: 500,
letterSpacing: '0.32px',
},
body2: {
fontSize: '14px',
fontWeight: 500,
letterSpacing: '0.28px',
},
caption: {
fontSize: '12px',
fontWeight: 400,
letterSpacing: '0.24px',
},
},
shape: {
borderRadius: 8,
},
});
export default theme;
import React from 'react';
import { Box, Typography, LinearProgress, Paper } from '@mui/material';
import { styled } from '@mui/material/styles';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faClock } from '@fortawesome/free-solid-svg-icons';
import { YourCourse, formatCompletionPercentage, formatTimeLeft } from '../../data/coursesDashboardMockData';
const CourseCardContainer = styled(Paper)(({ theme }) => ({
borderRadius: '12px',
border: `1px solid ${theme.palette.grey[100]}`,
boxShadow: '0px 1px 2px rgba(228, 229, 231, 0.24)',
padding: '20px',
display: 'flex',
flexDirection: 'column',
gap: '16px',
backgroundColor: theme.palette.background.default,
}));
const CourseIcon = styled('img')({
width: '40px',
height: '40px',
borderRadius: '8px',
});
const CourseTitle = styled(Typography)(({ theme }) => ({
fontSize: '16px',
fontWeight: 600,
letterSpacing: '0.32px',
color: theme.palette.text.primary,
}));
const CourseDetails = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: '6px',
flexWrap: 'wrap',
}));
const CompletionText = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 400,
letterSpacing: '0.28px',
color: theme.palette.text.secondary,
}));
const DotSeparator = styled(Box)(({ theme }) => ({
width: '4px',
height: '4px',
borderRadius: '50%',
backgroundColor: theme.palette.grey[300],
}));
const TimeContainer = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: '4px',
}));
const ClockIconWrapper = styled(Box)(({ theme }) => ({
width: '13px',
height: '13px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.text.secondary,
}));
const ProgressContainer = styled(Box)({
width: '100%',
});
const StyledLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: '10px',
borderRadius: '3px',
backgroundColor: theme.palette.grey[100],
'& .MuiLinearProgress-bar': {
backgroundColor: theme.palette.success.main,
borderRadius: '3px',
},
}));
interface CourseCardProps {
course: YourCourse;
}
const CourseCard: React.FC<CourseCardProps> = ({ course }) => {
return (
<CourseCardContainer elevation={0}>
<CourseIcon src={course.icon} alt={course.title} />
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
<CourseTitle>{course.title}</CourseTitle>
<CourseDetails>
<CompletionText>
{formatCompletionPercentage(course.completionPercentage)}
</CompletionText>
<DotSeparator />
<TimeContainer>
<ClockIconWrapper>
<FontAwesomeIcon icon={faClock} size="xs" />
</ClockIconWrapper>
<CompletionText>
{formatTimeLeft(course.timeLeft)}
</CompletionText>
</TimeContainer>
</CourseDetails>
<ProgressContainer>
<StyledLinearProgress
variant="determinate"
value={course.completionPercentage}
/>
</ProgressContainer>
</Box>
</CourseCardContainer>
);
};
export default CourseCard;
import React from 'react';
import { Box, Typography, Divider, Paper } from '@mui/material';
import { styled } from '@mui/material/styles';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faClock, faStar } from '@fortawesome/free-solid-svg-icons';
import { AllCourse, formatRating, formatEnrollmentCount, formatDuration } from '../../data/coursesDashboardMockData';
const AllCourseCardContainer = styled(Paper)(({ theme }) => ({
borderRadius: '12px',
border: `1px solid ${theme.palette.grey[100]}`,
boxShadow: '0px 1px 2px rgba(228, 229, 231, 0.24)',
overflow: 'hidden',
backgroundColor: theme.palette.background.default,
}));
const CourseImage = styled('img')({
width: '100%',
height: '150px',
objectFit: 'cover',
});
const CourseContent = styled(Box)({
padding: '16px',
display: 'flex',
flexDirection: 'column',
gap: '12px',
});
const CourseTitle = styled(Typography)(({ theme }) => ({
fontSize: '16px',
fontWeight: 600,
letterSpacing: '0.32px',
color: theme.palette.text.primary,
}));
const CourseDescription = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 400,
letterSpacing: '0.28px',
lineHeight: '21px',
color: theme.palette.text.secondary,
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: 2,
overflow: 'hidden',
}));
const StyledDivider = styled(Divider)(({ theme }) => ({
border: `1px dashed ${theme.palette.divider}`,
}));
const CourseFooter = styled(Box)({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
});
const LeftSection = styled(Box)({
display: 'flex',
alignItems: 'center',
gap: '8px',
});
const DifficultyBadge = styled(Box)<{ difficultyColor: string }>(({ theme, difficultyColor }) => ({
display: 'flex',
alignItems: 'center',
gap: '2px',
padding: '4px 8px',
borderRadius: '4px',
backgroundColor: difficultyColor,
}));
const DifficultyText = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 400,
letterSpacing: '0.28px',
color: theme.palette.text.secondary,
}));
const DotSeparator = styled(Box)(({ theme }) => ({
width: '4px',
height: '4px',
borderRadius: '50%',
backgroundColor: theme.palette.grey[300],
}));
const TimeContainer = styled(Box)({
display: 'flex',
alignItems: 'center',
gap: '4px',
});
const ClockIconWrapper = styled(Box)(({ theme }) => ({
width: '13px',
height: '13px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.text.secondary,
}));
const RatingSection = styled(Box)({
display: 'flex',
alignItems: 'center',
gap: '2px',
});
const StarIconWrapper = styled(Box)(({ theme }) => ({
width: '13px',
height: '12px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.warning.main,
}));
const RatingText = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 500,
letterSpacing: '0.28px',
color: theme.palette.text.primary,
}));
const EnrollmentText = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 400,
letterSpacing: '0.28px',
color: theme.palette.text.secondary,
}));
interface AllCourseCardProps {
course: AllCourse;
}
const AllCourseCard: React.FC<AllCourseCardProps> = ({ course }) => {
return (
<AllCourseCardContainer elevation={0}>
<CourseImage src={course.image} alt={course.title} />
<CourseContent>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
<CourseTitle>{course.title}</CourseTitle>
<CourseDescription>{course.description}</CourseDescription>
</Box>
<StyledDivider />
<CourseFooter>
<LeftSection>
<DifficultyBadge difficultyColor={course.difficultyColor}>
<DifficultyText>{course.difficulty}</DifficultyText>
</DifficultyBadge>
<DotSeparator />
<TimeContainer>
<ClockIconWrapper>
<FontAwesomeIcon icon={faClock} size="xs" />
</ClockIconWrapper>
<DifficultyText>
{formatDuration(course.duration)}
</DifficultyText>
</TimeContainer>
</LeftSection>
<RatingSection>
<StarIconWrapper>
<FontAwesomeIcon icon={faStar} size="xs" />
</StarIconWrapper>
<RatingText>{formatRating(course.rating)}</RatingText>
<EnrollmentText>
{formatEnrollmentCount(course.enrollmentCount)}
</EnrollmentText>
</RatingSection>
</CourseFooter>
</CourseContent>
</AllCourseCardContainer>
);
};
export default AllCourseCard;
import React from 'react';
import { Box, Typography, Button } from '@mui/material';
import { styled } from '@mui/material/styles';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faObjectUngroup, faAngleDown } from '@fortawesome/free-solid-svg-icons';
const FiltersContainer = styled(Box)({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: '16px',
});
const LeftSection = styled(Box)({
display: 'flex',
alignItems: 'center',
gap: '16px',
});
const FilterButton = styled(Button)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: '6px',
padding: '8px 12px',
borderRadius: '8px',
border: `1px solid ${theme.palette.divider}`,
boxShadow: '0px 1px 2px rgba(82, 88, 102, 0.06)',
backgroundColor: theme.palette.background.default,
color: theme.palette.text.secondary,
textTransform: 'none',
fontSize: '14px',
fontWeight: 500,
letterSpacing: '0.28px',
'&:hover': {
backgroundColor: theme.palette.grey[50],
},
}));
const SectionTitle = styled(Typography)(({ theme }) => ({
fontSize: '20px',
fontWeight: 600,
color: theme.palette.text.primary,
}));
const SortButton = styled(Button)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: '6px',
padding: '8px 12px',
borderRadius: '8px',
border: `1px solid ${theme.palette.divider}`,
boxShadow: '0px 1px 2px rgba(82, 88, 102, 0.06)',
backgroundColor: theme.palette.background.default,
color: theme.palette.text.secondary,
textTransform: 'none',
fontSize: '14px',
fontWeight: 500,
letterSpacing: '0.28px',
'&:hover': {
backgroundColor: theme.palette.grey[50],
},
}));
const FilterIconWrapper = styled(Box)(({ theme }) => ({
width: '16px',
height: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.text.secondary,
}));
const DropdownIconWrapper = styled(Box)(({ theme }) => ({
width: '10px',
height: '6px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.text.secondary,
}));
interface CoursesFiltersAndSortingProps {
onFilterChange?: (filter: string) => void;
onSortChange?: (sort: string) => void;
}
const CoursesFiltersAndSorting: React.FC<CoursesFiltersAndSortingProps> = ({
onFilterChange,
onSortChange
}) => {
return (
<FiltersContainer>
<LeftSection>
<FilterButton onClick={() => onFilterChange?.('filters')}>
<FilterIconWrapper>
<FontAwesomeIcon icon={faObjectUngroup} size="sm" />
</FilterIconWrapper>
Filters
</FilterButton>
<SectionTitle>All Courses</SectionTitle>
</LeftSection>
<SortButton onClick={() => onSortChange?.('recommendation')}>
Recommendation
<DropdownIconWrapper>
<FontAwesomeIcon icon={faAngleDown} size="xs" />
</DropdownIconWrapper>
</SortButton>
</FiltersContainer>
);
};
export default CoursesFiltersAndSorting;
import React, { useState, useMemo } from 'react';
import { Box, CssBaseline, Typography } from '@mui/material';
import { ThemeProvider, useColorScheme } from '@mui/material/styles';
import { styled } from '@mui/material/styles';
import Sidebar from '../Sidebar/Sidebar';
import Header from '../Header/Header';
import CourseCard from '../CourseCard/CourseCard';
import AllCourseCard from '../AllCourseCard/AllCourseCard';
import CoursesFiltersAndSorting from '../CoursesFiltersAndSorting/CoursesFiltersAndSorting';
import { CoursesDashboardProps, AllCourse } from '../../data/coursesDashboardMockData';
import theme from '../../theme/theme';
const DashboardContainer = styled(Box)(({ theme }) => ({
display: 'flex',
minHeight: '100vh',
width: '100vw',
backgroundColor: theme.palette.background.default,
}));
const MainContent = styled(Box)(({ theme }) => ({
flex: 1,
display: 'flex',
flexDirection: 'column',
width: 'calc(100vw - 270px)',
overflow: 'hidden',
}));
const ContentArea = styled(Box)(({ theme }) => ({
padding: '24px 32px',
display: 'flex',
flexDirection: 'column',
gap: '24px',
width: '100%',
}));
const YourCoursesSection = styled(Box)({
display: 'flex',
flexDirection: 'column',
gap: '16px',
});
const SectionTitle = styled(Typography)(({ theme }) => ({
fontSize: '20px',
fontWeight: 600,
color: theme.palette.text.primary,
}));
const YourCoursesGrid = styled(Box)({
display: 'flex',
gap: '24px',
flexWrap: 'wrap',
});
const AllCoursesSection = styled(Box)({
display: 'flex',
flexDirection: 'column',
gap: '24px',
});
const AllCoursesGrid = styled(Box)(({ theme }) => ({
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '24px',
width: '100%',
'@media (max-width: 1200px)': {
gridTemplateColumns: 'repeat(2, 1fr)',
},
'@media (max-width: 800px)': {
gridTemplateColumns: '1fr',
},
}));
const CoursesDashboard: React.FC<CoursesDashboardProps> = ({ user, yourCourses, allCourses }) => {
const [sortBy, setSortBy] = useState('Recommendation');
const [filterBy, setFilterBy] = useState('All');
const { mode, setMode } = useColorScheme();
const handleToggleDarkMode = () => {
setMode(mode === 'light' ? 'dark' : 'light');
};
const handleSortChange = (newSortBy: string) => {
setSortBy(newSortBy);
};
const handleFilterChange = (newFilterBy: string) => {
setFilterBy(newFilterBy);
};
const sortedAndFilteredCourses = useMemo(() => {
let filteredCourses = [...allCourses];
// Apply sorting
switch (sortBy) {
case 'Most Recent':
filteredCourses = filteredCourses.reverse();
break;
case 'Highest Rated':
filteredCourses.sort((a, b) => b.rating - a.rating);
break;
case 'Duration':
filteredCourses.sort((a, b) => a.duration - b.duration);
break;
case 'Recommendation':
default:
// Default order (recommendation)
break;
}
return filteredCourses;
}, [allCourses, sortBy, filterBy]);
return (
<DashboardContainer>
<CssBaseline />
<Sidebar
darkMode={mode === 'dark'}
onToggleDarkMode={handleToggleDarkMode}
/>
<MainContent>
<Header user={user} />
<ContentArea>
<YourCoursesSection>
<SectionTitle>Your Courses</SectionTitle>
<YourCoursesGrid>
{yourCourses.map((course) => (
<CourseCard key={course.id} course={course} />
))}
</YourCoursesGrid>
</YourCoursesSection>
<AllCoursesSection>
<CoursesFiltersAndSorting
onSortChange={handleSortChange}
onFilterChange={handleFilterChange}
/>
<AllCoursesGrid>
{sortedAndFilteredCourses.map((course) => (
<AllCourseCard key={course.id} course={course} />
))}
</AllCoursesGrid>
</AllCoursesSection>
</ContentArea>
</MainContent>
</DashboardContainer>
);
};
const CoursesDashboardWithTheme: React.FC<CoursesDashboardProps> = (props) => {
return (
<ThemeProvider theme={theme}>
<CoursesDashboard {...props} />
</ThemeProvider>
);
};
export default CoursesDashboardWithTheme;
import React from 'react';
import { Box, Typography, Avatar, IconButton, Divider } from '@mui/material';
import { styled } from '@mui/material/styles';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';
import NotificationIcon from '../Icons/NotificationIcon.svg';
const HeaderContainer = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '24px 32px',
borderBottom: `1px solid ${theme.palette.grey[100]}`,
backgroundColor: theme.palette.background.default,
}));
const HeaderTitle = styled(Typography)(({ theme }) => ({
fontSize: '24px',
fontWeight: 600,
color: theme.palette.text.primary,
}));
const IconGroup = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: '16px',
}));
const ProfileSection = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: '8px',
}));
const UserInfo = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
}));
const UserName = styled(Typography)(({ theme }) => ({
fontSize: '12px',
fontWeight: 600,
letterSpacing: '0.24px',
color: theme.palette.text.primary,
}));
const UserRole = styled(Typography)(({ theme }) => ({
fontSize: '12px',
fontWeight: 400,
letterSpacing: '0.24px',
color: theme.palette.text.secondary,
}));
const SearchIconButton = styled(IconButton)(({ theme }) => ({
width: 20,
height: 20,
color: theme.palette.text.secondary,
}));
const NotificationIconButton = styled(IconButton)(({ theme }) => ({
width: 32,
height: 32,
borderRadius: '50%',
backgroundColor: theme.palette.grey[100],
'&:hover': {
backgroundColor: theme.palette.grey[200],
}
}));
interface HeaderProps {
user: {
name: string;
role: string;
avatar: string;
};
title?: string;
}
const Header: React.FC<HeaderProps> = ({ user, title = "Course" }) => {
return (
<HeaderContainer>
<HeaderTitle>{title}</HeaderTitle>
<IconGroup>
<SearchIconButton>
<FontAwesomeIcon icon={faMagnifyingGlass} size="sm" />
</SearchIconButton>
<Divider orientation="vertical" flexItem sx={{ height: 21, mx: 1 }} />
<ProfileSection>
<NotificationIconButton>
<NotificationIcon width={16} height={16} />
</NotificationIconButton>
<Avatar
src={user.avatar}
sx={{
width: 32,
height: 32,
backgroundColor: '#fbdfb1'
}}
/>
<UserInfo>
<UserName>{user.name}</UserName>
<UserRole>{user.role}</UserRole>
</UserInfo>
</ProfileSection>
</IconGroup>
</HeaderContainer>
);
};
export default Header;
import React from "react";
import {
Box,
Paper,
ButtonBase,
Divider,
Typography,
Switch,
FormControlLabel,
} from "@mui/material";
import { styled } from "@mui/material/styles";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faHouseChimneyWindow,
faBookmark,
faGraduationCap,
faPlay,
faLightbulb,
faAward,
faMicrochip,
faCalendarDays,
faEarthAfrica,
faSun,
faCircleQuestion,
} from "@fortawesome/free-solid-svg-icons";
import AcademicLogo from "../Icons/AcademicLogo.svg";
const SidebarContainer = styled(Paper)(({ theme }) => ({
width: 270,
height: "100vh",
minHeight: "100vh",
borderRadius: 0,
borderRight: `1px solid ${theme.palette.divider}`,
display: "flex",
flexDirection: "column",
padding: "16px",
gap: "16px",
backgroundColor: theme.palette.background.paper,
position: "sticky",
top: 0,
left: 0,
}));
const MenuSection = styled(Box)(({ theme }) => ({
display: "flex",
flexDirection: "column",
gap: "4px",
}));
const MenuItem = styled(ButtonBase)(({ theme }) => ({
display: "flex",
alignItems: "center",
gap: "10px",
padding: "8px 10px",
borderRadius: "10px",
width: "100%",
justifyContent: "flex-start",
minHeight: "40px",
"&.active": {
backgroundColor: theme.palette.grey[50],
"& .menu-text": {
fontWeight: 600,
color: theme.palette.text.primary,
},
},
}));
const MenuText = styled(Typography)(({ theme }) => ({
fontSize: "16px",
fontWeight: 500,
letterSpacing: "0.32px",
color: theme.palette.text.secondary,
}));
const IconWrapper = styled(Box)(({ theme }) => ({
width: 17,
height: 17,
display: "flex",
alignItems: "center",
justifyContent: "center",
color: theme.palette.text.secondary,
}));
interface SidebarProps {
darkMode: boolean;
onToggleDarkMode: () => void;
}
const Sidebar: React.FC<SidebarProps> = ({ darkMode, onToggleDarkMode }) => {
const menuItems = [
{ icon: faHouseChimneyWindow, label: "Home", active: false },
{ icon: faBookmark, label: "Bookmarks", active: false },
];
const mainMenuItems = [
{ icon: faGraduationCap, label: "Courses", active: true },
{ icon: faPlay, label: "Tutorials", active: false },
{ icon: faLightbulb, label: "Best Practices", active: false },
{ icon: faAward, label: "Certifications", active: false },
];
const resourceMenuItems = [
{ icon: faMicrochip, label: "Resources", active: false },
{ icon: faCalendarDays, label: "Events", active: false },
{ icon: faEarthAfrica, label: "Community", active: false },
];
const bottomMenuItems = [
{ icon: faSun, label: "Setting", active: false },
{ icon: faCircleQuestion, label: "Help Center", active: false },
];
return (
<SidebarContainer elevation={0}>
<Box sx={{ display: "flex", alignItems: "center", gap: 2, mb: 2 }}>
<img
src="/Logo_Academic.png"
alt="Academic Logo"
style={{ height: 32 }}
/>
</Box>
<MenuSection>
{menuItems.map((item, index) => (
<MenuItem key={index} className={item.active ? "active" : ""}>
<IconWrapper>
<FontAwesomeIcon icon={item.icon} />
</IconWrapper>
<MenuText className="menu-text">{item.label}</MenuText>
</MenuItem>
))}
</MenuSection>
<Divider />
<MenuSection>
{mainMenuItems.map((item, index) => (
<MenuItem key={index} className={item.active ? "active" : ""}>
<IconWrapper>
<FontAwesomeIcon icon={item.icon} />
</IconWrapper>
<MenuText className="menu-text">{item.label}</MenuText>
</MenuItem>
))}
</MenuSection>
<Divider />
<MenuSection>
{resourceMenuItems.map((item, index) => (
<MenuItem key={index} className={item.active ? "active" : ""}>
<IconWrapper>
<FontAwesomeIcon icon={item.icon} />
</IconWrapper>
<MenuText className="menu-text">{item.label}</MenuText>
</MenuItem>
))}
</MenuSection>
<Box sx={{ flexGrow: 1 }} />
<MenuSection>
{bottomMenuItems.map((item, index) => (
<MenuItem key={index} className={item.active ? "active" : ""}>
<IconWrapper>
<FontAwesomeIcon icon={item.icon} />
</IconWrapper>
<MenuText className="menu-text">{item.label}</MenuText>
</MenuItem>
))}
</MenuSection>
<Box
sx={{
mt: 2,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "10px",
borderRadius: "10px",
width: "100%",
}}
>
<MenuText>Dark Mode</MenuText>
<Switch checked={darkMode} onChange={onToggleDarkMode} size="small" />
</Box>
</SidebarContainer>
);
};
export default Sidebar;
import React from 'react';
import CoursesDashboard from './src/components/CoursesDashboard/CoursesDashboard';
import { mockRootProps } from './src/data/coursesDashboardMockData';
const App: React.FC = () => {
return (
<CoursesDashboard
user={mockRootProps.user}
yourCourses={mockRootProps.yourCourses}
allCourses={mockRootProps.allCourses}
/>
);
};
export default App;
import React, { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import Example from './App.coursesdashboard'
import './index.css'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<Example />
</StrictMode>,
)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Courses Dashboard</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>
I've finished generating all the code files for the courses dashboard. Here's a brief summary of the files generated:
src/data/coursesDashboardMockData.ts - Mock data file containing:
src/theme/theme.ts - Updated theme with course-specific colors and styling
src/components/CourseCard/CourseCard.tsx - Progress card component for "Your Courses" section featuring:
src/components/AllCourseCard/AllCourseCard.tsx - Detailed course card for "All Courses" section with:
src/components/CoursesFiltersAndSorting/CoursesFiltersAndSorting.tsx - Header component with:
src/components/CoursesDashboard/CoursesDashboard.tsx - Main dashboard component integrating:
src/components/Header/Header.tsx - Updated to accept dynamic title prop
src/components/Sidebar/Sidebar.tsx - Updated to show "Courses" as active instead of "Tutorials"
App.coursesdashboard.tsx - Preview app file for the courses dashboard
The implementation includes all the key features:
import React from 'react';
import { Box, Typography, Avatar, IconButton, Divider } from '@mui/material';
import { styled } from '@mui/material/styles';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faMagnifyingGlass, faBell } from '@fortawesome/free-solid-svg-icons';
const HeaderContainer = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '24px 32px',
borderBottom: `1px solid ${theme.palette.grey[100]}`,
backgroundColor: theme.palette.background.default,
}));
const HeaderTitle = styled(Typography)(({ theme }) => ({
fontSize: '24px',
fontWeight: 600,
color: theme.palette.text.primary,
}));
const IconGroup = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: '16px',
}));
const ProfileSection = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: '8px',
}));
const UserInfo = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
}));
const UserName = styled(Typography)(({ theme }) => ({
fontSize: '12px',
fontWeight: 600,
letterSpacing: '0.24px',
color: theme.palette.text.primary,
}));
const UserRole = styled(Typography)(({ theme }) => ({
fontSize: '12px',
fontWeight: 400,
letterSpacing: '0.24px',
color: theme.palette.text.secondary,
}));
const SearchIconButton = styled(IconButton)(({ theme }) => ({
width: 20,
height: 20,
color: theme.palette.text.secondary,
}));
const NotificationIconButton = styled(IconButton)(({ theme }) => ({
width: 32,
height: 32,
borderRadius: '50%',
backgroundColor: theme.palette.grey[100],
'&:hover': {
backgroundColor: theme.palette.grey[200],
}
}));
interface HeaderProps {
user: {
name: string;
role: string;
avatar: string;
};
title?: string;
}
const Header: React.FC<HeaderProps> = ({ user, title = "Course" }) => {
return (
<HeaderContainer>
<HeaderTitle>{title}</HeaderTitle>
<IconGroup>
<SearchIconButton>
<FontAwesomeIcon icon={faMagnifyingGlass} size="sm" />
</SearchIconButton>
<Divider orientation="vertical" flexItem sx={{ height: 21, mx: 1 }} />
<ProfileSection>
<NotificationIconButton>
<FontAwesomeIcon icon={faBell} size="sm" />
</NotificationIconButton>
<Avatar
src={user.avatar}
sx={{
width: 32,
height: 32,
backgroundColor: '#fbdfb1'
}}
/>
<UserInfo>
<UserName>{user.name}</UserName>
<UserRole>{user.role}</UserRole>
</UserInfo>
</ProfileSection>
</IconGroup>
</HeaderContainer>
);
};
export default Header;
Fixed the missing NotificationIcon.svg import by replacing it with FontAwesome's faBell icon.
import React, { useState, useMemo } from 'react';
import { Box, CssBaseline, Typography } from '@mui/material';
import { ThemeProvider, useColorScheme } from '@mui/material/styles';
import { styled } from '@mui/material/styles';
import Sidebar from '../Sidebar/Sidebar';
import Header from '../Header/Header';
import CourseCard from '../CourseCard/CourseCard';
import AllCourseCard from '../AllCourseCard/AllCourseCard';
import CoursesFiltersAndSorting from '../CoursesFiltersAndSorting/CoursesFiltersAndSorting';
import { CoursesDashboardProps, AllCourse } from '../../data/coursesDashboardMockData';
import theme from '../../theme/theme';
const DashboardContainer = styled(Box)(({ theme }) => ({
display: 'flex',
minHeight: '100vh',
width: '100vw',
backgroundColor: theme.palette.background.default,
}));
const MainContent = styled(Box)(({ theme }) => ({
flex: 1,
display: 'flex',
flexDirection: 'column',
width: 'calc(100vw - 270px)',
overflow: 'hidden',
}));
const ContentArea = styled(Box)(({ theme }) => ({
padding: '24px 32px',
display: 'flex',
flexDirection: 'column',
gap: '24px',
width: '100%',
}));
const YourCoursesSection = styled(Box)({
display: 'flex',
flexDirection: 'column',
gap: '16px',
});
const SectionTitle = styled(Typography)(({ theme }) => ({
fontSize: '20px',
fontWeight: 600,
color: theme.palette.text.primary,
}));
const YourCoursesGrid = styled(Box)({
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '24px',
width: '100%',
});
const AllCoursesSection = styled(Box)({
display: 'flex',
flexDirection: 'column',
gap: '24px',
});
const AllCoursesGrid = styled(Box)(({ theme }) => ({
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '24px',
width: '100%',
'@media (max-width: 1200px)': {
gridTemplateColumns: 'repeat(2, 1fr)',
},
'@media (max-width: 800px)': {
gridTemplateColumns: '1fr',
},
}));
const CoursesDashboard: React.FC<CoursesDashboardProps> = ({ user, yourCourses, allCourses }) => {
const [sortBy, setSortBy] = useState('Recommendation');
const [filterBy, setFilterBy] = useState('All');
const { mode, setMode } = useColorScheme();
const handleToggleDarkMode = () => {
setMode(mode === 'light' ? 'dark' : 'light');
};
const handleSortChange = (newSortBy: string) => {
setSortBy(newSortBy);
};
const handleFilterChange = (newFilterBy: string) => {
setFilterBy(newFilterBy);
};
const sortedAndFilteredCourses = useMemo(() => {
let filteredCourses = [...allCourses];
// Apply sorting
switch (sortBy) {
case 'Most Recent':
filteredCourses = filteredCourses.reverse();
break;
case 'Highest Rated':
filteredCourses.sort((a, b) => b.rating - a.rating);
break;
case 'Duration':
filteredCourses.sort((a, b) => a.duration - b.duration);
break;
case 'Recommendation':
default:
// Default order (recommendation)
break;
}
return filteredCourses;
}, [allCourses, sortBy, filterBy]);
return (
<DashboardContainer>
<CssBaseline />
<Sidebar
darkMode={mode === 'dark'}
onToggleDarkMode={handleToggleDarkMode}
/>
<MainContent>
<Header user={user} />
<ContentArea>
<YourCoursesSection>
<SectionTitle>Your Courses</SectionTitle>
<YourCoursesGrid>
{yourCourses.map((course) => (
<CourseCard key={course.id} course={course} />
))}
</YourCoursesGrid>
</YourCoursesSection>
<AllCoursesSection>
<CoursesFiltersAndSorting
onSortChange={handleSortChange}
onFilterChange={handleFilterChange}
/>
<AllCoursesGrid>
{sortedAndFilteredCourses.map((course) => (
<AllCourseCard key={course.id} course={course} />
))}
</AllCoursesGrid>
</AllCoursesSection>
</ContentArea>
</MainContent>
</DashboardContainer>
);
};
const CoursesDashboardWithTheme: React.FC<CoursesDashboardProps> = (props) => {
return (
<ThemeProvider theme={theme}>
<CoursesDashboard {...props} />
</ThemeProvider>
);
};
export default CoursesDashboardWithTheme;
Fixed the "Your Courses" section to use a grid layout that spans the full width with equal-sized cards.
import React from 'react';
import { Box, Typography, Divider, Paper } from '@mui/material';
import { styled } from '@mui/material/styles';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faClock, faStar, faSignal } from '@fortawesome/free-solid-svg-icons';
import { AllCourse, formatRating, formatEnrollmentCount, formatDuration } from '../../data/coursesDashboardMockData';
const AllCourseCardContainer = styled(Paper)(({ theme }) => ({
borderRadius: '12px',
border: `1px solid ${theme.palette.grey[100]}`,
boxShadow: '0px 1px 2px rgba(228, 229, 231, 0.24)',
overflow: 'hidden',
backgroundColor: theme.palette.background.default,
}));
const CourseImage = styled('img')({
width: '100%',
height: '150px',
objectFit: 'cover',
});
const CourseContent = styled(Box)({
padding: '16px',
display: 'flex',
flexDirection: 'column',
gap: '12px',
});
const CourseTitle = styled(Typography)(({ theme }) => ({
fontSize: '16px',
fontWeight: 600,
letterSpacing: '0.32px',
color: theme.palette.text.primary,
}));
const CourseDescription = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 400,
letterSpacing: '0.28px',
lineHeight: '21px',
color: theme.palette.text.secondary,
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: 2,
overflow: 'hidden',
}));
const StyledDivider = styled(Divider)(({ theme }) => ({
border: `1px dashed ${theme.palette.divider}`,
}));
const CourseFooter = styled(Box)({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
});
const LeftSection = styled(Box)({
display: 'flex',
alignItems: 'center',
gap: '8px',
});
const DifficultyBadge = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: '2px',
}));
const DifficultyText = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 400,
letterSpacing: '0.28px',
color: theme.palette.text.secondary,
}));
const DifficultyIconWrapper = styled(Box)(({ theme }) => ({
width: '16px',
height: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.text.secondary,
}));
const DotSeparator = styled(Box)(({ theme }) => ({
width: '4px',
height: '4px',
borderRadius: '50%',
backgroundColor: theme.palette.grey[300],
}));
const TimeContainer = styled(Box)({
display: 'flex',
alignItems: 'center',
gap: '4px',
});
const ClockIconWrapper = styled(Box)(({ theme }) => ({
width: '13px',
height: '13px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.text.secondary,
}));
const RatingSection = styled(Box)({
display: 'flex',
alignItems: 'center',
gap: '2px',
});
const StarIconWrapper = styled(Box)(({ theme }) => ({
width: '13px',
height: '12px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.warning.main,
}));
const RatingText = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 500,
letterSpacing: '0.28px',
color: theme.palette.text.primary,
}));
const EnrollmentText = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 400,
letterSpacing: '0.28px',
color: theme.palette.text.secondary,
}));
interface AllCourseCardProps {
course: AllCourse;
}
const AllCourseCard: React.FC<AllCourseCardProps> = ({ course }) => {
return (
<AllCourseCardContainer elevation={0}>
<CourseImage src={course.image} alt={course.title} />
<CourseContent>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
<CourseTitle>{course.title}</CourseTitle>
<CourseDescription>{course.description}</CourseDescription>
</Box>
<StyledDivider />
<CourseFooter>
<LeftSection>
<DifficultyBadge>
<DifficultyIconWrapper>
<FontAwesomeIcon icon={faSignal} size="xs" />
</DifficultyIconWrapper>
<DifficultyText>{course.difficulty}</DifficultyText>
</DifficultyBadge>
<DotSeparator />
<TimeContainer>
<ClockIconWrapper>
<FontAwesomeIcon icon={faClock} size="xs" />
</ClockIconWrapper>
<DifficultyText>
{formatDuration(course.duration)}
</DifficultyText>
</TimeContainer>
</LeftSection>
<RatingSection>
<StarIconWrapper>
<FontAwesomeIcon icon={faStar} size="xs" />
</StarIconWrapper>
<RatingText>{formatRating(course.rating)}</RatingText>
<EnrollmentText>
{formatEnrollmentCount(course.enrollmentCount)}
</EnrollmentText>
</RatingSection>
</CourseFooter>
</CourseContent>
</AllCourseCardContainer>
);
};
export default AllCourseCard;
Fixed the difficulty badges to remove colored backgrounds and added signal strength icons to match the original Figma design.
Open this conversation in your favorite IDE
// Course difficulty levels and completion status
export enum CourseDifficulty {
BEGINNER = 'Beginner',
INTERMEDIATE = 'Intermediate',
ADVANCED = 'Advanced'
}
export enum CourseStatus {
IN_PROGRESS = 'in_progress',
COMPLETED = 'completed',
NOT_STARTED = 'not_started'
}
export const formatCompletionPercentage = (percentage: number): string => {
return `${percentage}% complete`;
};
export const formatTimeLeft = (hours: number): string => {
if (hours === 0) return '0 hours left';
if (hours < 1) return `${hours} hours left`;
return `${Math.floor(hours)} hours left`;
};
export const formatRating = (rating: number): string => {
return rating.toFixed(1);
};
export const formatEnrollmentCount = (count: number): string => {
return `(${count.toLocaleString()})`;
};
export const formatDuration = (hours: number): string => {
return `${hours} hours`;
};
// Props types (data passed to components)
export interface YourCourse {
id: string;
title: string;
icon: string;
completionPercentage: number;
timeLeft: number;
progressColor: string;
}
export interface AllCourse {
id: string;
title: string;
description: string;
image: string;
difficulty: string;
duration: number;
rating: number;
enrollmentCount: number;
difficultyColor: string;
}
export interface User {
name: string;
role: string;
avatar: string;
}
export interface CoursesDashboardProps {
user: User;
yourCourses: YourCourse[];
allCourses: AllCourse[];
}
// Data for the courses dashboard
export const mockRootProps = {
user: {
name: "Alesia K.",
role: "Pro Member",
avatar: "https://i.pravatar.cc/150?img=1"
},
yourCourses: [
{
id: "1",
title: "Design Accessibility",
icon: "/images/course-icon-1.png",
completionPercentage: 25,
timeLeft: 3,
progressColor: "#2c8f88"
},
{
id: "2",
title: "UX Research",
icon: "/images/course-icon-2.png",
completionPercentage: 70,
timeLeft: 1.5,
progressColor: "#2c8f88"
},
{
id: "3",
title: "Design Terminology",
icon: "/images/course-icon-3.png",
completionPercentage: 100,
timeLeft: 0,
progressColor: "#2c8f88"
}
],
allCourses: [
{
id: "4",
title: "Design Workshop Facilitation",
description: "Master your skills in design workshop facilitation and learn how to promote collaboration and find the best design solutions. Learn what activities, tools, and deliverables can enhance your workshops.",
image: "/images/course-image-1.png",
difficulty: "Advanced",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#2c8f88"
},
{
id: "5",
title: "Animation for Beginner",
description: "Procreate Dreams has transformed my ability to make animations from my art. Yet when I first opened the app, I was so frustrated: I didn't understand what I could actually do and was stumped when it didn't behave like I thought it would. Cut to today, I've fallen in love with animation as an art form and am blown away with how easily I can create something cool. Even beginners can create impressive results.",
image: "/images/course-image-2.png",
difficulty: "Beginner",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#8b5cf6"
},
{
id: "6",
title: "Common Design Pattern",
description: "Master your skills in design workshop facilitation and learn how to promote collaboration and find the best design solutions. Learn what activities, tools, and deliverables can enhance your workshops.",
image: "/images/course-image-3.png",
difficulty: "Intermediate",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#3b82f6"
},
{
id: "7",
title: "3D Design Foundation",
description: "Learn about the many applications of 3D design and get familiar with the basics of 3D, such as the XYZ coordinate system, 3D shape primitives, perception, lighting, and materials.",
image: "/images/course-image-4.png",
difficulty: "Advanced",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#8b5cf6"
},
{
id: "8",
title: "Framer for Beginner",
description: "Master your skills in design workshop facilitation and learn how to promote collaboration and find the best design solutions. Learn what activities, tools, and deliverables can enhance your workshops.",
image: "/images/course-image-5.png",
difficulty: "Beginner",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#1f2937"
},
{
id: "9",
title: "Design Workshop Facilitation",
description: "Master your skills in design workshop facilitation and learn how to promote collaboration and find the best design solutions. Learn what activities, tools, and deliverables can enhance your workshops.",
image: "/images/course-image-6.png",
difficulty: "Advanced",
duration: 6,
rating: 4.9,
enrollmentCount: 1890,
difficultyColor: "#f97316"
}
]
};
Read-onlyPlease wait while we set everything up