Add routeable lesson URLs for bootcamp pages
Implement deep linking to individual lessons with URL pattern:
- Route: /bootcamp/{product-slug}/{lessonId}
- lessonId parameter is optional for backward compatibility
- When no lessonId provided, defaults to first lesson
- Clicking lessons updates URL without page reload
- URL parameter drives lesson selection on page load
Changes:
- Update App.tsx route to accept optional :lessonId parameter
- Add lessonId extraction in Bootcamp.tsx useParams
- Implement handleSelectLesson to update URL on lesson click
- Update lesson selection logic to read from URL parameter
- Fallback to first lesson if lessonId not found
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -57,7 +57,7 @@ const App = () => (
|
||||
<Route path="/products/:slug" element={<ProductDetail />} />
|
||||
<Route path="/checkout" element={<Checkout />} />
|
||||
<Route path="/events" element={<Events />} />
|
||||
<Route path="/bootcamp/:slug" element={<Bootcamp />} />
|
||||
<Route path="/bootcamp/:slug/:lessonId?" element={<Bootcamp />} />
|
||||
<Route path="/webinar/:slug" element={<WebinarRecording />} />
|
||||
<Route path="/consulting" element={<ConsultingBooking />} />
|
||||
<Route path="/calendar" element={<CalendarPage />} />
|
||||
|
||||
@@ -55,7 +55,7 @@ interface UserReview {
|
||||
}
|
||||
|
||||
export default function Bootcamp() {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
const { slug, lessonId } = useParams<{ slug: string; lessonId?: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { user, loading: authLoading } = useAuth();
|
||||
|
||||
@@ -134,7 +134,20 @@ export default function Bootcamp() {
|
||||
}));
|
||||
setModules(sortedModules);
|
||||
|
||||
if (sortedModules.length > 0 && sortedModules[0].lessons.length > 0) {
|
||||
// Select lesson based on URL parameter or default to first lesson
|
||||
const allLessons = sortedModules.flatMap(m => m.lessons);
|
||||
|
||||
if (lessonId) {
|
||||
// Find the lesson by ID from URL
|
||||
const lessonFromUrl = allLessons.find(l => l.id === lessonId);
|
||||
if (lessonFromUrl) {
|
||||
setSelectedLesson(lessonFromUrl);
|
||||
} else if (allLessons.length > 0) {
|
||||
// If lesson not found, default to first lesson
|
||||
setSelectedLesson(allLessons[0]);
|
||||
}
|
||||
} else if (allLessons.length > 0 && sortedModules[0].lessons.length > 0) {
|
||||
// No lessonId in URL, select first lesson
|
||||
setSelectedLesson(sortedModules[0].lessons[0]);
|
||||
}
|
||||
}
|
||||
@@ -170,6 +183,12 @@ export default function Bootcamp() {
|
||||
return progress.some(p => p.lesson_id === lessonId);
|
||||
};
|
||||
|
||||
const handleSelectLesson = (lesson: Lesson) => {
|
||||
setSelectedLesson(lesson);
|
||||
// Update URL without full page reload
|
||||
navigate(`/bootcamp/${slug}/${lesson.id}`);
|
||||
};
|
||||
|
||||
const markAsCompleted = async () => {
|
||||
if (!selectedLesson || !user || !product) return;
|
||||
|
||||
@@ -347,7 +366,7 @@ export default function Bootcamp() {
|
||||
key={lesson.id}
|
||||
onClick={() => {
|
||||
if (isReleased) {
|
||||
setSelectedLesson(lesson);
|
||||
handleSelectLesson(lesson);
|
||||
setMobileMenuOpen(false);
|
||||
}
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user