feat: add shareable lesson links with URL routing

- Add share button with SVG link icon in lesson title row
- Create share dialog with copy URL functionality
- Implement URL hash-based routing for lesson navigation
- Support browser back/forward navigation
- Add i18n translations for share dialog in all languages
- Position share button between title and completion badge
- Add RTL support for title row layout

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
2026-01-14 21:35:49 +01:00
parent ede3e56a5a
commit 5083032735
5 changed files with 364 additions and 53 deletions

57
src/helpers/router.js Normal file
View File

@@ -0,0 +1,57 @@
/**
* URL Router for Code Crispies
* Handles hash-based routing for shareable lesson links
* Format: #module-id/lesson-index (e.g., #flexbox/2)
*/
/**
* Parse current URL hash into module and lesson info
* @returns {{ moduleId: string, lessonIndex: number } | null}
*/
export function parseHash() {
const hash = window.location.hash.slice(1); // Remove '#'
if (!hash) return null;
const parts = hash.split("/");
if (parts.length !== 2) return null;
const moduleId = parts[0];
const lessonIndex = parseInt(parts[1], 10);
if (!moduleId || isNaN(lessonIndex) || lessonIndex < 0) return null;
return { moduleId, lessonIndex };
}
/**
* Update URL hash with history entry (for navigation)
* @param {string} moduleId
* @param {number} lessonIndex
*/
export function updateHash(moduleId, lessonIndex) {
const newHash = `#${moduleId}/${lessonIndex}`;
if (window.location.hash !== newHash) {
history.pushState(null, "", newHash);
}
}
/**
* Replace URL hash without history entry (for invalid URL fallbacks)
* @param {string} moduleId
* @param {number} lessonIndex
*/
export function replaceHash(moduleId, lessonIndex) {
const newHash = `#${moduleId}/${lessonIndex}`;
history.replaceState(null, "", newHash);
}
/**
* Build full shareable URL for current lesson
* @param {string} moduleId
* @param {number} lessonIndex
* @returns {string}
*/
export function getShareableUrl(moduleId, lessonIndex) {
const base = window.location.origin + window.location.pathname;
return `${base}#${moduleId}/${lessonIndex}`;
}