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="/products/:slug" element={<ProductDetail />} />
|
||||||
<Route path="/checkout" element={<Checkout />} />
|
<Route path="/checkout" element={<Checkout />} />
|
||||||
<Route path="/events" element={<Events />} />
|
<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="/webinar/:slug" element={<WebinarRecording />} />
|
||||||
<Route path="/consulting" element={<ConsultingBooking />} />
|
<Route path="/consulting" element={<ConsultingBooking />} />
|
||||||
<Route path="/calendar" element={<CalendarPage />} />
|
<Route path="/calendar" element={<CalendarPage />} />
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ interface UserReview {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Bootcamp() {
|
export default function Bootcamp() {
|
||||||
const { slug } = useParams<{ slug: string }>();
|
const { slug, lessonId } = useParams<{ slug: string; lessonId?: string }>();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { user, loading: authLoading } = useAuth();
|
const { user, loading: authLoading } = useAuth();
|
||||||
|
|
||||||
@@ -134,7 +134,20 @@ export default function Bootcamp() {
|
|||||||
}));
|
}));
|
||||||
setModules(sortedModules);
|
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]);
|
setSelectedLesson(sortedModules[0].lessons[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,6 +183,12 @@ export default function Bootcamp() {
|
|||||||
return progress.some(p => p.lesson_id === lessonId);
|
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 () => {
|
const markAsCompleted = async () => {
|
||||||
if (!selectedLesson || !user || !product) return;
|
if (!selectedLesson || !user || !product) return;
|
||||||
|
|
||||||
@@ -347,7 +366,7 @@ export default function Bootcamp() {
|
|||||||
key={lesson.id}
|
key={lesson.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isReleased) {
|
if (isReleased) {
|
||||||
setSelectedLesson(lesson);
|
handleSelectLesson(lesson);
|
||||||
setMobileMenuOpen(false);
|
setMobileMenuOpen(false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user