diff --git a/package.json b/package.json index e20b867b..1aa84ba9 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@supabase/supabase-js": "^2.49.4", "autoprefixer": "^10.4.21", "axios": "^1.8.4", + "lucide-react": "^0.511.0", "next": "15.3.1", "postcss": "^8.5.3", "react": "^19.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70b7442b..087459bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: axios: specifier: ^1.8.4 version: 1.8.4 + lucide-react: + specifier: ^0.511.0 + version: 0.511.0(react@19.1.0) next: specifier: 15.3.1 version: 15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -1904,6 +1907,11 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lucide-react@0.511.0: + resolution: {integrity: sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -4594,6 +4602,10 @@ snapshots: dependencies: js-tokens: 4.0.0 + lucide-react@0.511.0(react@19.1.0): + dependencies: + react: 19.1.0 + math-intrinsics@1.1.0: {} meow@12.1.1: {} diff --git a/public/assets/icons/article.svg b/public/assets/icons/article.svg new file mode 100644 index 00000000..431f0773 --- /dev/null +++ b/public/assets/icons/article.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/icons/blue-completion.svg b/public/assets/icons/blue-completion.svg new file mode 100644 index 00000000..38a291f9 --- /dev/null +++ b/public/assets/icons/blue-completion.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/icons/blue-play.svg b/public/assets/icons/blue-play.svg new file mode 100644 index 00000000..d8c1810c --- /dev/null +++ b/public/assets/icons/blue-play.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/icons/blue-star.svg b/public/assets/icons/blue-star.svg new file mode 100644 index 00000000..cfbe9976 --- /dev/null +++ b/public/assets/icons/blue-star.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/icons/bookmark.svg b/public/assets/icons/bookmark.svg new file mode 100644 index 00000000..6cb5552f --- /dev/null +++ b/public/assets/icons/bookmark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/assets/icons/clock.svg b/public/assets/icons/clock.svg new file mode 100644 index 00000000..e02fa631 --- /dev/null +++ b/public/assets/icons/clock.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/icons/completion.svg b/public/assets/icons/completion.svg new file mode 100644 index 00000000..e3522ea5 --- /dev/null +++ b/public/assets/icons/completion.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/icons/document.svg b/public/assets/icons/document.svg new file mode 100644 index 00000000..2b74c0e6 --- /dev/null +++ b/public/assets/icons/document.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/assets/icons/download-file.svg b/public/assets/icons/download-file.svg new file mode 100644 index 00000000..ed5458c2 --- /dev/null +++ b/public/assets/icons/download-file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/icons/eye.svg b/public/assets/icons/eye.svg new file mode 100644 index 00000000..60c83224 --- /dev/null +++ b/public/assets/icons/eye.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/icons/heart.svg b/public/assets/icons/heart.svg new file mode 100644 index 00000000..2d9c3126 --- /dev/null +++ b/public/assets/icons/heart.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/assets/icons/number-1.svg b/public/assets/icons/number-1.svg new file mode 100644 index 00000000..369a3f4f --- /dev/null +++ b/public/assets/icons/number-1.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/icons/number-2.svg b/public/assets/icons/number-2.svg new file mode 100644 index 00000000..d71da882 --- /dev/null +++ b/public/assets/icons/number-2.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/icons/number-3.svg b/public/assets/icons/number-3.svg new file mode 100644 index 00000000..2390cd09 --- /dev/null +++ b/public/assets/icons/number-3.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/icons/number-4.svg b/public/assets/icons/number-4.svg new file mode 100644 index 00000000..a8b1b5e4 --- /dev/null +++ b/public/assets/icons/number-4.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/icons/number-5.svg b/public/assets/icons/number-5.svg new file mode 100644 index 00000000..14505a23 --- /dev/null +++ b/public/assets/icons/number-5.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/assets/icons/people.svg b/public/assets/icons/people.svg new file mode 100644 index 00000000..9ca99be2 --- /dev/null +++ b/public/assets/icons/people.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/icons/play.svg b/public/assets/icons/play.svg new file mode 100644 index 00000000..3356d48e --- /dev/null +++ b/public/assets/icons/play.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/icons/sort.svg b/public/assets/icons/sort.svg new file mode 100644 index 00000000..6c933a00 --- /dev/null +++ b/public/assets/icons/sort.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/icons/star.svg b/public/assets/icons/star.svg new file mode 100644 index 00000000..ef74db5a --- /dev/null +++ b/public/assets/icons/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/icons/un-star.svg b/public/assets/icons/un-star.svg new file mode 100644 index 00000000..f8b34424 --- /dev/null +++ b/public/assets/icons/un-star.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/icons/upload-file.svg b/public/assets/icons/upload-file.svg new file mode 100644 index 00000000..3f6d8681 --- /dev/null +++ b/public/assets/icons/upload-file.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/assets/images/avatar.png b/public/assets/images/avatar.png new file mode 100644 index 00000000..f141124b Binary files /dev/null and b/public/assets/images/avatar.png differ diff --git a/public/assets/images/banner.png b/public/assets/images/banner.png new file mode 100644 index 00000000..a6a4a320 Binary files /dev/null and b/public/assets/images/banner.png differ diff --git a/public/assets/images/happy-face.png b/public/assets/images/happy-face.png new file mode 100644 index 00000000..d19dc20c Binary files /dev/null and b/public/assets/images/happy-face.png differ diff --git a/public/assets/images/suggested-course.png b/public/assets/images/suggested-course.png new file mode 100644 index 00000000..95992cac Binary files /dev/null and b/public/assets/images/suggested-course.png differ diff --git a/src/app/test/page.tsx b/src/app/test/page.tsx new file mode 100644 index 00000000..9c218e34 --- /dev/null +++ b/src/app/test/page.tsx @@ -0,0 +1,50 @@ +import CourseCard from '@/components/common/ui/CourseCard'; +import CourseContent from '@/components/common/ui/CourseContent'; +import CourseDetail from '@/components/common/ui/CourseDetail'; +import InstructorInfo from '@/components/common/ui/InstuctorInfo'; +import OverView from '@/components/common/ui/OverView'; +import PublisherCard from '@/components/common/ui/PublisherCard'; +import Rating from '@/components/common/ui/Rating'; +import Review from '@/components/common/ui/Review'; +import SuggestedCourse from '@/components/common/ui/SuggestedCourse'; +import Image from 'next/image'; +import React from 'react'; + +function page() { + return ( +
+
+ {/* LEFT: Nội dung chính khóa học */} +
+ {/* Banner */} + Banner + {/* Info: Giảng viên + Like + Share */} +
+ +
+ {/* Mô tả */} + + {/* Course Detail */} + + {/* Review */} + +
+ {/* RIGHT: Sidebar */} +
+ {/* Hộp giá tiền + nút mua */} + + {/* Đánh giá Rating */} + + {/* Publisher Info */} + + {/* Overview */} + + {/* Suggested Course */} + +
+
+
+ ); +} + +export default page; diff --git a/src/components/CourseCard.tsx b/src/components/common/CourseCard.tsx similarity index 100% rename from src/components/CourseCard.tsx rename to src/components/common/CourseCard.tsx diff --git a/src/components/common/ui/CourseCard.tsx b/src/components/common/ui/CourseCard.tsx new file mode 100644 index 00000000..81303300 --- /dev/null +++ b/src/components/common/ui/CourseCard.tsx @@ -0,0 +1,71 @@ +'use client'; + +import Image from 'next/image'; + +export default function CourseCard() { + return ( +
+
+ {/* LEFT: Phần giá */} +
+
+

Full course

+
800.000 VNĐ
+
+
+ {/* RIGHT: Tag giảm giá */} +
+ + 50% OFF + +
+
+ {/* Course includes */} +
+
+
400.000 VNĐ
+
Course includes
+
+ Play + 61 hours on-demand video +
+
+ Article6 Articles +
+
+ Download8 + Downloadable resources +
+
+ Document + Practice test +
+
+ Upload + Practical sharing article +
+
+ Certificate + Certificate of Completion +
+
+
+ {/* Buttons */} +
+ + Bookmark +
+ +
+ ); +} diff --git a/src/components/common/ui/CourseContent.tsx b/src/components/common/ui/CourseContent.tsx new file mode 100644 index 00000000..ba8aaa32 --- /dev/null +++ b/src/components/common/ui/CourseContent.tsx @@ -0,0 +1,110 @@ +'use client'; + +import { useState, JSX } from 'react'; +import { ChevronDown } from 'lucide-react'; +import Image from 'next/image'; + +export default function CourseContent() { + const [openSections, setOpenSections] = useState([]); + + interface Lecture { + title: string; + duration: string; + preview: boolean; + icon?: JSX.Element; + } + interface Section { + title: string; + lectures?: Lecture[] | number; + duration?: string; + } + + const sections: Section[] = [ + { + title: 'Course introduce', + lectures: [ + { + title: 'Course introduce', + duration: '03:28', + preview: true, + icon: number 1, + }, + { + title: 'Downloading Photoshop, Illustrator', + duration: '03:28', + preview: false, + icon: number 2, + }, + { + title: 'Settings and Preferences', + duration: '03:28', + preview: false, + icon: number 3, + }, + ], + }, + { + title: 'Basic Of Photoshop, Illustrator', + lectures: 3, + }, + { + title: 'How to Use the Pen Tool?', + lectures: 3, + }, + { + title: 'How to Use the Pen Tool?', + lectures: 3, + }, + ]; + + const toggleSection = (index: number) => { + setOpenSections(prev => + prev.includes(index) ? prev.filter(i => i !== index) : [...prev, index] + ); + }; + + return ( +
+

Course content

+ {sections.map((section, sectionKey) => ( +
+ + {openSections.includes(sectionKey) && Array.isArray(section.lectures) && ( +
+ {section.lectures.map((lecture, idx) => ( +
+
+ {lecture.icon} + {lecture.title} +
+
+ {lecture.preview && ( + + Preview + + )} + {lecture.duration} +
+
+ ))} +
+ )} +
+ ))} +
+ ); +} diff --git a/src/components/common/ui/CourseDetail.tsx b/src/components/common/ui/CourseDetail.tsx new file mode 100644 index 00000000..567c66ab --- /dev/null +++ b/src/components/common/ui/CourseDetail.tsx @@ -0,0 +1,41 @@ +'use client'; + +import Image from 'next/image'; + +export default function CourseDetail() { + return ( +
+
Course detail
+
+
+ Lession +
+ Play Icon + 128 +
+
+
+ Duration +
+ Clock Icon + 56h 28m +
+
+
+ Skill level +
+ Sort Icon + Beginner +
+
+
+ View +
+ Eye Icon + 12,450 +
+
+
+
+ ); +} diff --git a/src/components/common/ui/InstuctorInfo.tsx b/src/components/common/ui/InstuctorInfo.tsx new file mode 100644 index 00000000..1d989e1a --- /dev/null +++ b/src/components/common/ui/InstuctorInfo.tsx @@ -0,0 +1,59 @@ +'use client'; + +import Image from 'next/image'; + +export default function InstructorInfo() { + return ( +
+ {/* Header Section */} +

+ Graphic Design Master - Learn GREAT Design +

+ {/* Profile and Stats Row */} +
+ {/* Profile Info */} +
+ Avatar +
+
Đao Tuan Kiet
+
Instructional Expert
+
+
+ {/* Stats */} +
+
+ Heart Icon + 300 Like +
+
+ Share Icon + Share +
+
+
+ {/* Description Section */} +

Description

+
+

+ We also learn the basics of Adobe Photoshop, Illustrator and InDesign and do projects with + real world applications. Every designer needs to know and master these programs and this + course makes sure you know the essential tools to power through amazing projects. +

+

+ In Adobe Photoshop, we will review photo editing and manipulation techniques like how to + cut objects out, duotones, changing color on objects, the liquify tool and we will create + a compelling YouTube thumbnail with{' '} +

+ + View all > + +
+
+ ); +} diff --git a/src/components/common/ui/OverView.tsx b/src/components/common/ui/OverView.tsx new file mode 100644 index 00000000..b1895d67 --- /dev/null +++ b/src/components/common/ui/OverView.tsx @@ -0,0 +1,60 @@ +import Image from 'next/image'; + +export default function OverView() { + return ( +
+
+

Overview

+
+

+ Graphic Design Mastercla - Learn GREAT Design +

+
+ {[ + 'Typography', + 'Logo & Branding', + 'AI Tools', + 'Illustration', + 'InDesign', + 'Photo Editing', + ].map((tag, idx) => ( + + {tag} + + ))} +
+

+ A comprehensive design course with 6 key learning areas: Graphic Design, Photo & Video + Editing, UI/UX & Web Design, Creative Skills, Trends & Tools, and 3D & Motion — equipping + learners with essential skills, creative thinking, and modern tools to thrive in the digital + design industry. +

+
The course will have stages:
+
+
+ Star Icon + Model Detailing +
+
+ Star Icon + Basics of Working in Blender +
+
+ Star Icon + Learning Lighting Techniques +
+
+ Star Icon + Creating Animations +
+
+ Star Icon + Applying Textures +
+
+
+ ); +} diff --git a/src/components/common/ui/PublisherCard.tsx b/src/components/common/ui/PublisherCard.tsx new file mode 100644 index 00000000..a4deaa90 --- /dev/null +++ b/src/components/common/ui/PublisherCard.tsx @@ -0,0 +1,50 @@ +import Image from 'next/image'; + +export default function PublisherCard() { + return ( +
+
+

Publisher

+ + View profile + +
+
+
+ Avatar +
+
+

Đào Tuấn Kiệt

+

Instructional Expert

+
+
+

+ Hey! My name is Kiet, I’m 26 and I’m a freelance 2D Artist with around four years of + experience +

+
+
+ Star Icon + 4.8 Instructor rating +
+
+ Star Icon{' '} + 889 Reviews +
+
+ Star Icon 4,886 + Students +
+
+ Star Icon 8 + Courses +
+
+
+ ); +} diff --git a/src/components/common/ui/Rating.tsx b/src/components/common/ui/Rating.tsx new file mode 100644 index 00000000..cbc31c50 --- /dev/null +++ b/src/components/common/ui/Rating.tsx @@ -0,0 +1,37 @@ +'use client'; + +import Image from 'next/image'; + +export default function Rating() { + return ( +
+
+ {/* Left: Avatar + Info */} +
+ Avatar +
+
Rating
+
2,492 Students
+
+
+ {/* Right: Stars + Rating */} +
+
+ Star Icon + Star Icon + Star Icon + Star Icon + Star Icon +
+
4.8 (880 rating)
+
+
+
+ ); +} diff --git a/src/components/common/ui/Review.tsx b/src/components/common/ui/Review.tsx new file mode 100644 index 00000000..dc431554 --- /dev/null +++ b/src/components/common/ui/Review.tsx @@ -0,0 +1,61 @@ +'use client'; + +import Image from 'next/image'; +import { Star, ThumbsUp, ThumbsDown } from 'lucide-react'; + +const reviews = Array(4).fill({ + name: 'Dao Tuan Kiet', + date: '2 weeks ago', + content: + "An extensive and thorough course on ChatGPT, AI and many other API's. I will use the course as a reference in the future as there is a ton of great information. An impressive work.", + rating: 4.5, +}); + +export default function Review() { + return ( +
+

Review and rating

+
+ {reviews.map((review, index) => ( +
+ {/* Header */} +
+
+ Avatar +
+

{review.name}

+

{review.date}

+
+
+
+
+ {/* Content */} +

+ {review.content} +

+ {/* Footer */} +
+
+ {review.rating} + +
+
+ + +
+
+
+ ))} +
+
+ ); +} diff --git a/src/components/common/ui/SuggestedCourse.tsx b/src/components/common/ui/SuggestedCourse.tsx new file mode 100644 index 00000000..85d1c653 --- /dev/null +++ b/src/components/common/ui/SuggestedCourse.tsx @@ -0,0 +1,43 @@ +import Image from 'next/image'; +import { Star } from 'lucide-react'; + +export default function SuggestedCourse() { + const courses = Array(5).fill({ + title: 'Figma UI UX Design Essentials', + author: 'By Dao Tuan Kiet', + rating: '4.5', + image: '/assets/images/suggested-course.png', + }); + + return ( +
+
+

Suggested Course

+ + View all + +
+
+ {courses.map((course, index) => ( +
+ course +
+

{course.title}

+

{course.author}

+
+ {course.rating} + +
+
+
+ ))} +
+
+ ); +}