@@ -179,8 +179,8 @@ export default function PrivacyPolicy() {
Age Requirements
- You must be at least 13 years old to use InnoVision.
- If you're a parent and believe your child under 13 has created an account, please contact us
+ You must be at least 13 years old to use InnoVision.
+ If you're a parent and believe your child under 13 has created an account, please contact us
immediately at privacy@innovision.com
diff --git a/src/app/profile/certificates/page.jsx b/src/app/profile/certificates/page.jsx
new file mode 100644
index 0000000..2e9ab61
--- /dev/null
+++ b/src/app/profile/certificates/page.jsx
@@ -0,0 +1,139 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { useAuth } from "@/contexts/auth";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Button } from "@/components/ui/button";
+import { Award, Eye } from "lucide-react";
+import { toast } from "sonner";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog";
+import CertificateGenerator from "@/components/certificates/CertificateGenerator";
+
+export default function CertificatesPage() {
+ const { user } = useAuth();
+ const [certificates, setCertificates] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [selectedCertificate, setSelectedCertificate] = useState(null);
+ const [showDialog, setShowDialog] = useState(false);
+
+ useEffect(() => {
+ if (user?.email) {
+ fetchCertificates();
+ }
+ }, [user]);
+
+ const fetchCertificates = async () => {
+ try {
+ const response = await fetch(`/api/certificates/${encodeURIComponent(user.email)}`);
+ const data = await response.json();
+
+ if (data.success) {
+ setCertificates(data.certificates);
+ }
+ } catch (error) {
+ console.error("Error fetching certificates:", error);
+ toast.error("Failed to load certificates");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const viewCertificate = (cert) => {
+ setSelectedCertificate(cert);
+ setShowDialog(true);
+ };
+
+ if (loading) {
+ return (
+
+
+
+
+
Loading certificates...
+
+
+
+ );
+ }
+
+ return (
+
+
+
My Certificates
+
+ View and download your course completion certificates
+
+
+
+ {certificates.length === 0 ? (
+
+
+
+
No Certificates Yet
+
+ Complete a course to earn your first certificate!
+
+
+
+
+ ) : (
+
+ {certificates.map((cert) => (
+
+
+
+
+
+ {cert.completionDate}
+
+
+
+ {cert.courseTitle}
+
+
+ {cert.chapterCount} chapters completed
+
+
+
+
+
+ Certificate ID: {cert.certificateId}
+
+
+
+
+
+ ))}
+
+ )}
+
+
+
+ );
+}
diff --git a/src/app/profile/page.jsx b/src/app/profile/page.jsx
index 9e67d01..fd741eb 100644
--- a/src/app/profile/page.jsx
+++ b/src/app/profile/page.jsx
@@ -4,7 +4,8 @@ import { useState, useEffect } from "react";
import { useAuth } from "@/contexts/auth";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
-import { Trophy, TrendingUp, BookOpen, Calendar, Settings } from "lucide-react";
+import { Trophy, TrendingUp, BookOpen, Calendar, Settings, Award, ChevronLeft, ChevronRight } from "lucide-react";
+import { useRef } from "react";
import { Button } from "@/components/ui/button";
import Sidebar from "@/components/dashboard/Sidebar";
import { ProblemSolvedChart } from "@/components/ui/problem-sloved-chart";
@@ -27,6 +28,8 @@ import PremiumDialog from "@/components/PremiumDialog";
import LockedFeature from "@/components/LockedFeature";
import { useRouter } from "next/navigation";
import { PageBackground, GridPattern, ScrollReveal } from "@/components/ui/PageWrapper";
+import ChartSkeleton from "@/components/skeletons/ChartSkeleton";
+import CourseListSkeleton from "@/components/skeletons/CourseListSkeleton";
export default function ProfilePage() {
const { user } = useAuth();
@@ -42,6 +45,44 @@ export default function ProfilePage() {
const [premiumStatus, setPremiumStatus] = useState(null);
const [showPremiumDialog, setShowPremiumDialog] = useState(false);
const [blockedFeature, setBlockedFeature] = useState("");
+ const tabsListRef = useRef(null);
+ const [activeTab, setActiveTab] = useState("overview");
+
+ const tabOrder = ["overview", "progress", "courses", "activity", "compete", "certificates", "research", "settings"];
+
+ const navigateTab = (direction) => {
+ const currentIndex = tabOrder.indexOf(activeTab);
+ let nextIndex;
+ if (direction === "next") {
+ nextIndex = (currentIndex + 1) % tabOrder.length;
+ } else {
+ nextIndex = (currentIndex - 1 + tabOrder.length) % tabOrder.length;
+ }
+ setActiveTab(tabOrder[nextIndex]);
+ };
+
+ const scrollTabs = (direction) => {
+ if (tabsListRef.current) {
+ const scrollAmount = 200;
+ tabsListRef.current.scrollBy({
+ left: direction === "left" ? -scrollAmount : scrollAmount,
+ behavior: "smooth",
+ });
+ }
+ };
+
+ useEffect(() => {
+ if (tabsListRef.current) {
+ const activeElement = tabsListRef.current.querySelector('[data-state="active"]');
+ if (activeElement) {
+ activeElement.scrollIntoView({
+ behavior: "smooth",
+ inline: "center",
+ block: "nearest",
+ });
+ }
+ }
+ }, [activeTab]);
useEffect(() => {
fetchUser();
@@ -126,14 +167,14 @@ export default function ProfilePage() {
-
-
+
+
{/* Trial Banner */}
-
+
{/* Premium Dialog */}
-
@@ -150,47 +191,93 @@ export default function ProfilePage() {
{/* Main Content - All original tabs */}
-
-
-
-
- Overview
-
-
-
- Progress
-
-
-
- Courses
-
-
-
- Activity
-
-
-
- Compete
-
-
-
- Research
-
-
-
- Settings
-
-
+
+ {/* Tab Navigation Pager (Mobile Optimized) */}
+
+
+
+
+
+
+
+
+ Overview
+
+
+
+ Progress
+
+
+
+ Courses
+
+
+
+ Activity
+
+
+
+ Compete
+
+
+
+ Certificates
+
+
+
+ Research
+
+
+
+ Settings
+
+
+
+ {/* Fades for better visual scroll indication */}
+
+
+
+
+
+
+
+
+
{/* Overview Tab - Gamification Dashboard */}
{/* Motivational Quote */}
-
+
{/* Learning Stats Dashboard */}
{user?.email && }
-
+
{user?.email && }
@@ -199,11 +286,11 @@ export default function ProfilePage() {
{user?.email && }
-
+
{/* Badge Collection Gallery */}
-
@@ -211,17 +298,26 @@ export default function ProfilePage() {
{/* Progress Tab - XP Chart */}
-
-
- XP Earned
- Your XP earned data over the last year
-
-
-
-
-
+ {loading ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+ XP Earned
+ Your XP earned data over the last year
+
+
+
+
+
- {user?.email && }
+ {user?.email && }
+ >
+ )}
@@ -229,13 +325,17 @@ export default function ProfilePage() {
{/* Bookmarks Section */}
-
+
Recent Courses
-
+ {loading ? (
+
+ ) : (
+
+ )}
@@ -244,7 +344,11 @@ export default function ProfilePage() {
Completed Courses
-
+ {loading ? (
+
+ ) : (
+
+ )}
@@ -275,104 +379,124 @@ export default function ProfilePage() {
{/* Research Tab - Data Export */}
-
- {/* Interaction Dataset */}
-
-
-
-
- Interaction Dataset
-
-
- Anonymized user interaction data including clicks, time spent, and navigation patterns
-
-
-
-
-
-
-
Fully anonymized
+
+ {/* Interaction Dataset */}
+
+
+
+
+ Interaction Dataset
+
+
+ Anonymized user interaction data including clicks, time spent, and navigation patterns
+
+
+
+
+
+
+ Fully anonymized
+
+
+
+ JSON format
+
-
-
-
JSON format
+
+
+
+
+ {/* Outcome Dataset */}
+
+
+
+
+ Outcome Dataset
+
+
+ Learning outcomes, quiz scores, completion rates, and performance metrics
+
+
+
+
+
+
+ Fully anonymized
+
+
+
+ JSON format
+
-
-
-
-
+
+
+
+
- {/* Outcome Dataset */}
+ {/* Privacy Notice */}
-
- Outcome Dataset
+
+ Data Privacy & Anonymization
-
- Learning outcomes, quiz scores, completion rates, and performance metrics
-
-
-
-
+ All personal identifiers removed
+
+
+
+ User IDs replaced with random hashes
+
+
+
+ No email addresses or names included
+
+
+
+ Timestamps rounded to nearest hour
+
+
+
+ IP addresses excluded
+
+
+
+ GDPR and COPPA compliant
+
+
-
+
+
- {/* Privacy Notice */}
+ {/* Certificates Tab */}
+
-
- Data Privacy & Anonymization
+
+ My Certificates
+
+ View and download your course completion certificates
+
-
- -
-
- All personal identifiers removed
-
- -
-
- User IDs replaced with random hashes
-
- -
-
- No email addresses or names included
-
- -
-
- Timestamps rounded to nearest hour
-
- -
-
- IP addresses excluded
-
- -
-
- GDPR and COPPA compliant
-
-
+
-
{/* Settings Tab */}
diff --git a/src/app/research/page.jsx b/src/app/research/page.jsx
index 7e16573..409fba6 100644
--- a/src/app/research/page.jsx
+++ b/src/app/research/page.jsx
@@ -58,7 +58,7 @@ export default function ResearchPlatform() {
JSON format
-
-
);
diff --git a/src/app/roadmap/page.jsx b/src/app/roadmap/page.jsx
index ba9f12d..3a10210 100644
--- a/src/app/roadmap/page.jsx
+++ b/src/app/roadmap/page.jsx
@@ -1,109 +1,506 @@
"use client";
-import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
-import { Plus, BookOpen, Sparkles } from "lucide-react";
-import DeleteRoadmap from "@/components/Home/DeleteRoadmap";
+import { Card } from "@/components/ui/card";
+import { Button } from "@/components/ui/button";
+import { Plus, BookOpen, Sparkles, Search, X, Filter, Trash2, Archive, ArchiveRestore, CheckSquare } from "lucide-react";
+import CourseCard from "@/components/Home/CourseCard";
import Link from "next/link";
import { useState, useEffect } from "react";
import { Skeleton } from "@/components/ui/skeleton";
-import { loader } from "@/components/ui/Custom/ToastLoader";
import { PageBackground, GridPattern, PageHeader, ScrollReveal, HoverCard } from "@/components/ui/PageWrapper";
+import ChatBot from "@/components/chat/ChatBot";
+import { Input } from "@/components/ui/input";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+import { Checkbox } from "@/components/ui/checkbox";
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@/components/ui/alert-dialog";
+import { toast } from "sonner";
+import RecommendedCourses from "@/components/RecommendedCourses";
export default function page() {
+ const [error, setError] = useState(null);
const [roadmaps, setRoadmaps] = useState([]);
const [loading, setLoading] = useState(true);
- const { hideLoader } = loader();
+
+ // Filter states
+ const [searchQuery, setSearchQuery] = useState("");
+ const [difficultyFilter, setDifficultyFilter] = useState("all");
+ const [archiveFilter, setArchiveFilter] = useState("active"); // active, archived, all
+ const [sortBy, setSortBy] = useState("newest");
+
+ // Bulk selection states
+ const [selectionMode, setSelectionMode] = useState(false);
+ const [selectedCourses, setSelectedCourses] = useState([]);
+ const [bulkActionLoading, setBulkActionLoading] = useState(false);
+ const [showDeleteDialog, setShowDeleteDialog] = useState(false);
+ const [showArchiveDialog, setShowArchiveDialog] = useState(false);
+ const [bulkAction, setBulkAction] = useState(null);
+
+ // Load filter preferences from localStorage
+ useEffect(() => {
+ const savedFilters = localStorage.getItem("courseFilters");
+ if (savedFilters) {
+ const { search, difficulty, archive, sort } = JSON.parse(savedFilters);
+ setSearchQuery(search || "");
+ setDifficultyFilter(difficulty || "all");
+ setArchiveFilter(archive || "active");
+ setSortBy(sort || "newest");
+ }
+ }, []);
+
+ // Save filter preferences to localStorage
+ useEffect(() => {
+ localStorage.setItem(
+ "courseFilters",
+ JSON.stringify({
+ search: searchQuery,
+ difficulty: difficultyFilter,
+ archive: archiveFilter,
+ sort: sortBy,
+ })
+ );
+ }, [searchQuery, difficultyFilter, archiveFilter, sortBy]);
async function fetchRoadmaps() {
setLoading(true);
- const response = await fetch("/api/roadmap/all");
- const data = await response.json();
- setRoadmaps(data.docs);
- setLoading(false);
+ setError(null);
+
+ try {
+ const response = await fetch("/api/roadmap/all");
+
+ if (!response.ok) {
+ throw new Error("Failed to fetch roadmaps");
+ }
+
+ const data = await response.json();
+ setRoadmaps(data?.docs || []);
+ } catch (err) {
+ console.error("Roadmap fetch error:", err);
+ setError("Unable to load your courses.");
+ setRoadmaps([]);
+ } finally {
+ setLoading(false);
+ }
}
useEffect(() => {
fetchRoadmaps();
}, []);
+ const completedCourses = roadmaps.filter(r => r.process === "completed");
+
+ // Filter by archive status
+ const statusFilteredCourses = completedCourses.filter((course) => {
+ if (archiveFilter === "active") return !course.archived;
+ if (archiveFilter === "archived") return course.archived;
+ return true; // "all"
+ });
+
+ // Filter and sort courses
+ const filteredCourses = statusFilteredCourses
+ .filter((course) => {
+ // Archive filter - treat undefined/null as not archived
+ const isArchived = course.archived === true;
+ if (archiveFilter === "active" && isArchived) return false;
+ if (archiveFilter === "archived" && !isArchived) return false;
+ // "all" shows both
+
+ // Search filter
+ const matchesSearch = course.courseTitle
+ .toLowerCase()
+ .includes(searchQuery.toLowerCase());
+
+ // Difficulty filter
+ const matchesDifficulty =
+ difficultyFilter === "all" || course.difficulty === difficultyFilter;
+
+ return matchesSearch && matchesDifficulty;
+ })
+ .sort((a, b) => {
+ // Sort logic
+ if (sortBy === "newest") {
+ return new Date(b.createdAt) - new Date(a.createdAt);
+ } else if (sortBy === "oldest") {
+ return new Date(a.createdAt) - new Date(b.createdAt);
+ } else if (sortBy === "title") {
+ return a.courseTitle.localeCompare(b.courseTitle);
+ }
+ return 0;
+ });
+
+ // Clear all filters
+ const clearFilters = () => {
+ setSearchQuery("");
+ setDifficultyFilter("all");
+ setArchiveFilter("active");
+ setSortBy("newest");
+ };
+
+ // Check if any filters are active
+ const hasActiveFilters = searchQuery !== "" || difficultyFilter !== "all" || archiveFilter !== "active" || sortBy !== "newest";
+
+ // Bulk selection handlers
+ const toggleSelectionMode = () => {
+ setSelectionMode(!selectionMode);
+ setSelectedCourses([]);
+ };
+
+ const handleSelectCourse = (courseId, checked) => {
+ if (checked) {
+ setSelectedCourses([...selectedCourses, courseId]);
+ } else {
+ setSelectedCourses(selectedCourses.filter(id => id !== courseId));
+ }
+ };
+
+ const handleSelectAll = (checked) => {
+ if (checked) {
+ setSelectedCourses(filteredCourses.map(c => c.id));
+ } else {
+ setSelectedCourses([]);
+ }
+ };
+
+ const handleBulkAction = async (action) => {
+ if (selectedCourses.length === 0) {
+ toast.error("No courses selected");
+ return;
+ }
+
+ setBulkActionLoading(true);
+ try {
+ const response = await fetch("/api/roadmap/bulk", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ courseIds: selectedCourses,
+ action: action,
+ }),
+ });
+
+ const data = await response.json();
+
+ if (response.ok) {
+ toast.success(
+ `Successfully ${action}d ${data.processed} course${data.processed > 1 ? 's' : ''}${data.failed > 0 ? `, ${data.failed} failed` : ''
+ }`
+ );
+ setSelectedCourses([]);
+ setSelectionMode(false);
+ fetchRoadmaps();
+ } else {
+ toast.error(data.error || `Failed to ${action} courses`);
+ }
+ } catch (error) {
+ console.error(`Bulk ${action} error:`, error);
+ toast.error(`Failed to ${action} courses`);
+ } finally {
+ setBulkActionLoading(false);
+ setShowDeleteDialog(false);
+ setShowArchiveDialog(false);
+ }
+ };
+
+ const confirmBulkDelete = () => {
+ setBulkAction("delete");
+ setShowDeleteDialog(true);
+ };
+
+ const confirmBulkArchive = () => {
+ const hasArchivedCourses = selectedCourses.some(id => {
+ const course = filteredCourses.find(c => c.id === id);
+ return course?.archived;
+ });
+ setBulkAction(hasArchivedCourses ? "unarchive" : "archive");
+ setShowArchiveDialog(true);
+ };
+
return (
-
-
-
+ My Learning>}
/>
-
+
+ {!loading && !error && }
+
+ {/* Search and Filter Section */}
+ {!loading && !error && completedCourses.length > 0 && (
+
+ {/* Bulk Actions Toolbar */}
+ {selectionMode && (
+
+
+ 0}
+ onCheckedChange={handleSelectAll}
+ className="h-5 w-5"
+ />
+
+ {selectedCourses.length} of {filteredCourses.length} selected
+
+
+
+ {selectedCourses.length > 0 && (
+ <>
+
+
+ >
+ )}
+
+
+
+ )}
+
+ {/* Selection Mode Toggle */}
+ {!selectionMode && (
+
+
+
+ )}
+
+ {/* Search Bar */}
+
+
+ setSearchQuery(e.target.value)}
+ className="pl-10 pr-10 h-11 bg-card/50 backdrop-blur-sm border-border/50 focus:border-blue-500/50"
+ />
+ {searchQuery && (
+
+ )}
+
+
+ {/* Filters Row */}
+
+
+
+ Filters:
+
+
+ {/* Archive Status Filter */}
+
+
+ {/* Difficulty Filter */}
+
+
+ {/* Archive Filter */}
+
+
+ {/* Sort By */}
+
+
+ {/* Clear Filters Button */}
+ {hasActiveFilters && (
+
+ )}
+
+ {/* Course Count */}
+
+ Showing {filteredCourses.length} of {completedCourses.length} courses
+
+
+
+ )}
+
{loading ? (
Array(6)
.fill(0)
- .map((_, i) => {
- return (
-
- );
- })
+ .map((_, i) => (
+
+ ))
+ ) : error ? (
+
+
+
+
+ We couldn't load your courses
+
+
+ Please try again or refresh the page.
+
+
+
+
+ ) : completedCourses.length === 0 ? (
+
+
+
+
+ You don't have any courses yet
+
+
+ Start by generating your first roadmap
+
+
+
+
+
+
+ ) : filteredCourses.length === 0 ? (
+
+
+
+
+ No courses found
+
+
+ Try adjusting your search or filters
+
+
+
+
) : (
<>
- {roadmaps?.map((roadmap, index) => {
-
- if (roadmap.process === "completed") return (
-
-
-
-
-
- {roadmap?.courseTitle?.split(
- ":"
- )[0] || ""}
-
-
- {
- fetchRoadmaps();
- hideLoader();
- }}
- >
-
-
-
- {roadmap.courseDescription}
-
-
-
-
-
-
-
-
- );
- })}
-
r.process === "completed").length * 80}>
+ {/* Course Cards */}
+ {filteredCourses.map((roadmap, index) => (
+
+
+
+
+
+ ))}
+
+ {/* Create New Course Card */}
+
-
+
- Create your course
+ Create New Course
@@ -116,6 +513,55 @@ export default function page() {
)}
+
+
+ {/* Delete Confirmation Dialog */}
+
+
+
+ Delete {selectedCourses.length} Course{selectedCourses.length > 1 ? 's' : ''}?
+
+ This action cannot be undone. This will permanently delete the selected course{selectedCourses.length > 1 ? 's' : ''} and all associated data.
+
+
+
+ Cancel
+ handleBulkAction("delete")}
+ disabled={bulkActionLoading}
+ className="bg-red-600 hover:bg-red-700"
+ >
+ {bulkActionLoading ? "Deleting..." : "Delete"}
+
+
+
+
+
+ {/* Archive Confirmation Dialog */}
+
+
+
+
+ {bulkAction === "archive" ? "Archive" : "Unarchive"} {selectedCourses.length} Course{selectedCourses.length > 1 ? 's' : ''}?
+
+
+ {bulkAction === "archive"
+ ? `This will archive the selected course${selectedCourses.length > 1 ? 's' : ''}. You can restore them later from the archived filter.`
+ : `This will restore the selected course${selectedCourses.length > 1 ? 's' : ''} to your active courses.`
+ }
+
+
+
+ Cancel
+ handleBulkAction(bulkAction)}
+ disabled={bulkActionLoading}
+ >
+ {bulkActionLoading ? "Processing..." : bulkAction === "archive" ? "Archive" : "Unarchive"}
+
+
+
+
);
}
diff --git a/src/app/studio-course/[courseId]/[chapterId]/page.jsx b/src/app/studio-course/[courseId]/[chapterId]/page.jsx
index 3fd5ab3..65d1e8f 100644
--- a/src/app/studio-course/[courseId]/[chapterId]/page.jsx
+++ b/src/app/studio-course/[courseId]/[chapterId]/page.jsx
@@ -1,21 +1,25 @@
"use client";
import { useState, useEffect } from "react";
-import { useParams } from "next/navigation";
+import { useParams, useRouter } from "next/navigation";
import Sidebar from "@/components/sidebar/page";
import StudioContent from "@/components/chapter_content/StudioContent";
import { useAuth } from "@/contexts/auth";
import { useContext } from "react";
import xpContext from "@/contexts/xp";
+import { Button } from "@/components/ui/button";
+import { ArrowLeft, ArrowRight } from "lucide-react";
+import ChatBot from "@/components/chat/ChatBot";
export default function StudioCoursePage() {
const params = useParams();
+ const router = useRouter();
const { user } = useAuth();
const [course, setCourse] = useState(null);
const [courseData, setCourseData] = useState(null);
const [loading, setLoading] = useState(true);
const { awardXP } = useContext(xpContext);
const [viewAwarded, setViewAwarded] = useState(false);
-
+
const chapterIndex = parseInt(params.chapterId) - 1;
useEffect(() => {
@@ -24,11 +28,11 @@ export default function StudioCoursePage() {
// Award XP for viewing chapter
useEffect(() => {
- if (session && awardXP && !viewAwarded) {
+ if (user && awardXP && !viewAwarded) {
awardXP('view_course');
setViewAwarded(true);
}
- }, [session, awardXP, viewAwarded]);
+ }, [user, awardXP, viewAwarded]);
const fetchCourse = async () => {
try {
@@ -36,7 +40,7 @@ export default function StudioCoursePage() {
if (res.ok) {
const data = await res.json();
setCourseData(data); // Store original data
-
+
// Transform course data to match roadmap structure for Sidebar
const transformedData = {
courseTitle: data.title,
@@ -74,6 +78,21 @@ export default function StudioCoursePage() {
}
const currentChapter = courseData.chapters[chapterIndex];
+ const totalChapters = courseData.chapters.length;
+ const isFirstChapter = chapterIndex === 0;
+ const isLastChapter = chapterIndex >= totalChapters - 1;
+
+ const handlePrevChapter = () => {
+ if (!isFirstChapter) {
+ router.push(`/studio-course/${params.courseId}/${chapterIndex}`);
+ }
+ };
+
+ const handleNextChapter = () => {
+ if (!isLastChapter) {
+ router.push(`/studio-course/${params.courseId}/${chapterIndex + 2}`);
+ }
+ };
return (
@@ -88,8 +107,35 @@ export default function StudioCoursePage() {
+
+ {/* Chapter Navigation Buttons */}
+
+
+
+
+ Chapter {chapterIndex + 1} of {totalChapters}
+
+
+
+
+
);
}
diff --git a/src/app/studio/page.jsx b/src/app/studio/page.jsx
index 55c4a22..e26097a 100644
--- a/src/app/studio/page.jsx
+++ b/src/app/studio/page.jsx
@@ -18,8 +18,8 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
-import {
- BookOpen, Plus, Save, Eye, Upload, FileText,
+import {
+ BookOpen, Plus, Save, Eye, Upload, FileText,
Link as LinkIcon, Trash2, Crown, Sparkles
} from "lucide-react";
import { toast } from "sonner";
@@ -109,10 +109,10 @@ export default function StudioPage() {
};
try {
- const url = editingCourseId
- ? `/api/studio/courses/${editingCourseId}`
+ const url = editingCourseId
+ ? `/api/studio/courses/${editingCourseId}`
: "/api/studio/publish";
-
+
const method = editingCourseId ? "PUT" : "POST";
const res = await fetch(url, {
@@ -209,491 +209,491 @@ export default function StudioPage() {
-
+
-
Course Creator>}
- />
-
- {!premiumStatus.isPremium && (
-
-
-
-
-
-
-
-
Studio Preview Mode
-
- Free users can create 1 Studio course for testing. Upgrade to Premium for unlimited course creation and full design capabilities!
-
-
+
Course Creator>}
+ />
+
+ {!premiumStatus.isPremium && (
+
+
+
+
+
+
+
+
Studio Preview Mode
+
+ Free users can create 1 Studio course for testing. Upgrade to Premium for unlimited course creation and full design capabilities!
+
+
+
-
-
- )}
-
-
- {/* Left Sidebar - Course Info */}
-
-
-
- Course Details
-
-
-
-
- setCourseTitle(e.target.value)}
- placeholder="Enter course title"
- />
-
-
-
-
-
-
-
- {editingCourseId && (
-