diff --git a/package-lock.json b/package-lock.json index 39f105f..82bcc48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,11 @@ "name": "aplp.frontend.lms", "version": "0.0.0", "dependencies": { + "@radix-ui/react-collapsible": "^1.1.20", "@radix-ui/react-label": "^2.1.15", "@radix-ui/react-separator": "^1.1.15", "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-switch": "^1.3.7", "@radix-ui/react-tabs": "^1.1.21", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -590,6 +592,36 @@ "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", "license": "MIT" }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.20.tgz", + "integrity": "sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", @@ -822,6 +854,34 @@ } } }, + "node_modules/@radix-ui/react-switch": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.7.tgz", + "integrity": "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tabs": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz", @@ -935,6 +995,24 @@ } } }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", + "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz", diff --git a/package.json b/package.json index 34ed76b..2bb3fb2 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,11 @@ "preview": "vite preview" }, "dependencies": { + "@radix-ui/react-collapsible": "^1.1.20", "@radix-ui/react-label": "^2.1.15", "@radix-ui/react-separator": "^1.1.15", "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-switch": "^1.3.7", "@radix-ui/react-tabs": "^1.1.21", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/src/App.jsx b/src/App.jsx index eb20339..839cc18 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -3,6 +3,7 @@ import MainLayout from './layouts/MainLayout.jsx'; import CoursesPage from './pages/Course/CoursesPage.jsx'; import CourseDetail from './pages/Course/CourseDetails.jsx'; import LessonPage from './pages/lesson/LessonPage.jsx'; +import LessonFormPage from './pages/lesson/LessonFormPage.jsx'; import MCQPage from './pages/question-bank/MCQPage.jsx'; import ParagraphPage from './pages/question-bank/ParagraphPage.jsx'; import UsersPage from './pages/user/UsersPage.jsx'; @@ -20,6 +21,8 @@ function App() { } /> } /> } /> + } /> + } /> } /> } /> } /> diff --git a/src/components/ui/switch.jsx b/src/components/ui/switch.jsx new file mode 100644 index 0000000..90daa22 --- /dev/null +++ b/src/components/ui/switch.jsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import * as SwitchPrimitives from '@radix-ui/react-switch'; +import { cn } from '../../lib/utils'; + +const Switch = React.forwardRef(({ className, ...props }, ref) => ( + + + +)); +Switch.displayName = SwitchPrimitives.Root.displayName; + +export { Switch }; diff --git a/src/pages/Course/CourseDetails.jsx b/src/pages/Course/CourseDetails.jsx index e096fae..e155c2b 100644 --- a/src/pages/Course/CourseDetails.jsx +++ b/src/pages/Course/CourseDetails.jsx @@ -1,13 +1,30 @@ +import { useState } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; import { Badge } from '../../components/ui/badge'; import { Label } from '../../components/ui/label'; import { Input } from '../../components/ui/input'; import { Textarea } from '../../components/ui/textarea'; +import { Tabs, TabsList, TabsTrigger, TabsContent } from '../../components/ui/tabs'; +import { Separator } from '../../components/ui/separator'; import PageHeader from '../../components/PageHeader'; -import { useParams } from 'react-router-dom'; +import { + HiOutlinePlus, + HiOutlinePencil, + HiOutlineTrash, + HiOutlineEye, + HiOutlineChevronRight, + HiOutlineBookOpen, + HiOutlineVideoCamera, + HiOutlineDocumentText, + HiOutlineQuestionMarkCircle, + HiOutlineCodeBracket, + HiOutlineArrowUpTray, + HiOutlineFunnel, +} from 'react-icons/hi2'; -const data = [ +const coursesData = [ { code: 'LDR-101', title: 'Foundations of Modern Leadership', @@ -16,7 +33,7 @@ const data = [ status: 'Active', lastUpdate: '2026-08-19', image: - 'https://images.unsplash.com/photo-1556761175-b413da4baf72?auto=format&fit=crop&w=800&q=80' + 'https://images.unsplash.com/photo-1556761175-b413da4baf72?auto=format&fit=crop&w=800&q=80', }, { code: 'TEC-204', @@ -26,7 +43,7 @@ const data = [ status: 'Inactive', lastUpdate: '2026-08-19', image: - 'https://images.unsplash.com/photo-1533090161767-e6ffed986c88?auto=format&fit=crop&w=800&q=80' + 'https://images.unsplash.com/photo-1533090161767-e6ffed986c88?auto=format&fit=crop&w=800&q=80', }, { code: 'CMP-099', @@ -35,27 +52,166 @@ const data = [ status: 'Inactive', lastUpdate: '2026-08-19', image: - 'https://images.unsplash.com/photo-1497366754035-f200968a6e72?auto=format&fit=crop&w=800&q=80' - } + 'https://images.unsplash.com/photo-1497366754035-f200968a6e72?auto=format&fit=crop&w=800&q=80', + }, ]; +const lessonsByCourse = { + 'LDR-101': [ + { + id: 'les_001', + title: '1.1 What is Leadership?', + position: 1, + type: 'video', + duration: '12:30', + status: 'Published', + isFreePreview: true, + updatedAt: '2026-08-15', + }, + { + id: 'les_002', + title: '1.2 Leadership Styles Overview', + position: 2, + type: 'text', + duration: '~8 min read', + status: 'Published', + isFreePreview: false, + updatedAt: '2026-08-16', + }, + { + id: 'les_003', + title: '1.3 Self-Assessment Quiz', + position: 3, + type: 'quiz', + duration: '10:00', + status: 'Draft', + isFreePreview: false, + updatedAt: '2026-08-17', + }, + { + id: 'les_004', + title: '2.1 Communication Fundamentals', + position: 4, + type: 'video', + duration: '15:20', + status: 'Published', + isFreePreview: false, + updatedAt: '2026-08-18', + }, + { + id: 'les_005', + title: '2.2 Active Listening Techniques', + position: 5, + type: 'text', + duration: '~6 min read', + status: 'Draft', + isFreePreview: false, + updatedAt: '2026-08-19', + }, + ], + 'TEC-204': [ + { + id: 'les_006', + title: '1.1 Cloud Fundamentals Recap', + position: 1, + type: 'video', + duration: '18:45', + status: 'Published', + isFreePreview: true, + updatedAt: '2026-08-10', + }, + { + id: 'les_007', + title: '1.2 Microservices Architecture', + position: 2, + type: 'embed', + duration: 'Figma iFrame', + status: 'Draft', + isFreePreview: false, + updatedAt: '2026-08-12', + }, + ], + 'CMP-099': [], +}; + +const typeConfig = { + video: { label: 'Video', icon: HiOutlineVideoCamera, color: 'bg-blue-100 text-blue-700' }, + text: { label: 'Text', icon: HiOutlineDocumentText, color: 'bg-purple-100 text-purple-700' }, + quiz: { label: 'Quiz', icon: HiOutlineQuestionMarkCircle, color: 'bg-orange-100 text-orange-700' }, + embed: { label: 'Embed', icon: HiOutlineCodeBracket, color: 'bg-gray-100 text-gray-700' }, +}; + +const statusStyles = { + Published: 'bg-app-success text-app-success-text', + Draft: 'bg-app-neutral-badge text-app-neutral-badge-text', +}; + function CourseDetail() { const { courseId } = useParams(); - const course = data.find((e) => e.code === courseId); + const navigate = useNavigate(); + const course = coursesData.find((e) => e.code === courseId); + const lessons = lessonsByCourse[courseId] || []; + const [activeTab, setActiveTab] = useState('overview'); if (!course) { - return
Course not found
; + return ( + <> + +
+ +

Course not found

+ +
+ + ); } + + const publishedCount = lessons.filter((l) => l.status === 'Published').length; + const draftCount = lessons.filter((l) => l.status === 'Draft').length; + return ( <> -
-

{course?.title}

+ + {/* Breadcrumb */} +
+ + + {course.title} +
+ + {/* Header */} +
+
+

{course.title}

+ + {course.status} + +
-
- -
-

Course Details

-
-
-
- - + + + + {/* Tabs */} + + + + + Overview + + + + Lessons + + {lessons.length} + + + + + {/* Overview Tab */} + + +
+

Course Details

- - {course?.status === 'Active' ? ( - - Active - +
+
+ + +
+
+ + + {course.status} + +
+
+ +