Skip to content

Commit 724bf4b

Browse files
committed
documentation
1 parent 5b89a8c commit 724bf4b

10 files changed

Lines changed: 126 additions & 22 deletions

File tree

src/api/client.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
/**
2+
* API Client Module
3+
*
4+
* A centralized wrapper for making HTTP requests to the backend API.
5+
*
6+
* Key Features:
7+
* - **Base URL Resolution**: Automatically determines the API base URL based on environment (Vite env vars or window location).
8+
* - **Authentication**: Manages JWT tokens, automatically attaching them to requests and handling 401 Unauthorized responses.
9+
* - **CSRF Protection**: Automatically extracts and includes the `x-csrf-token` header for mutating requests.
10+
* - **AbortController Integration**: Supports request cancellation via `AbortController` signals.
11+
* - **Timeout Handling**: Implements request timeouts (default 15s) with cleanup.
12+
* - **Response Parsing**: Automatically parses JSON responses and handles edge cases like empty bodies or non-JSON errors.
13+
* - **File Uploads**: Provides a helper for `multipart/form-data` uploads.
14+
*/
115
export const getApiBaseUrl = () => {
216
if (import.meta.env.VITE_API_BASE_URL) {
317
return import.meta.env.VITE_API_BASE_URL.replace(/\/+$/, '')

src/components/Hero.jsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,45 @@ import { useContent } from '../context/ContentContext'
55
import { getIconComponent } from '../utils/iconMap'
66
import { scrollToSection } from '../utils/scrollToSection'
77
import { sanitizeExternalUrl } from '../utils/urlValidation'
8+
9+
/**
10+
* A generic Hero section component used for standard pages.
11+
*
12+
* Unlike the specialized `LandingHero`, this component is designed for reusability
13+
* across different pages. It supports a configurable title, subtitle, CTA buttons,
14+
* and a grid of feature highlights.
15+
*
16+
* Key Features:
17+
* - Dynamic Icon Resolution: Loads icons based on CMS string identifiers.
18+
* - Smart Navigation: Handles internal routes, external links, and anchor scrolling via `handleTarget`.
19+
* - Visuals: Features a background gradient with decorative blur blobs and a pattern overlay.
20+
*
21+
* @component
22+
* @example
23+
* // Usage within a page component
24+
* <Hero />
25+
*/
826
const Hero = () => {
927
const navigate = useNavigate()
1028
const location = useLocation()
1129
const { getSection } = useContent()
1230
const heroContent = getSection('hero') ?? {}
1331
const HeroIcon = useMemo(() => getIconComponent(heroContent.icon, 'Terminal'), [heroContent.icon])
1432
const features = Array.isArray(heroContent.features) ? heroContent.features : []
33+
34+
/**
35+
* Unified handler for processing navigation actions.
36+
*
37+
* Abstracts away the complexity of different link types:
38+
* - `section`: Scrolls to an ID on the home page (handling cross-page jumps if needed).
39+
* - `route`: Standard SPA client-side navigation.
40+
* - `external`: Opens in new tab with security attributes (noopener, noreferrer).
41+
* - `href`: Direct window location assignment (for special protocols or hard refreshes).
42+
*
43+
* @param {Object} target - The target definition object from CMS.
44+
* @param {string} target.type - The type of navigation ('section', 'route', 'external', 'href').
45+
* @param {string} target.value - The destination path, URL, or ID.
46+
*/
1547
const handleTarget = (target) => {
1648
if (!target || !target.type) {
1749
return

src/components/layout/Footer.jsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ const resolveContactFallbackIcon = (contact) => {
5555

5656
import EditableText from '../cms/EditableText'
5757

58+
/**
59+
* The global site footer component.
60+
*
61+
* Aggregates navigation links, contact info, and branding into a structured layout.
62+
*
63+
* Architecture:
64+
* - **Data Source**: Pulls `footer` section data and `navigation` structure from `ContentContext`.
65+
* - **Dynamic Links**: Merges specifically configured `quickLinks` with the global navigation tree.
66+
* - **CMS Integration**: All text elements (Brand, Copyright, Links) are wrapped in `EditableText`.
67+
* - **Safety**: Sanitizes all external URLs to prevent XSS/Phishing via `sanitizeExternalUrl`.
68+
*/
5869
const Footer = () => {
5970
const { getSection, navigation } = useContent()
6071
const footerContent = getSection('footer') ?? {}

src/context/AuthContext.jsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ import { api } from "../api/client"
44

55
const AuthContext = createContext(null)
66

7+
/**
8+
* Global Authentication Provider.
9+
*
10+
* Manages the user's session state, including login, logout, and token validation.
11+
*
12+
* Architecture:
13+
* - **Initialization**: Checks `/api/me` on mount to re-hydrate session from HTTP-only cookie.
14+
* - **Security**: Uses `AbortController` to prevent memory leaks during async auth checks.
15+
* - **Error Handling**: fails gracefully to "Unauthenticated" state on API errors (401/500).
16+
*/
717
export const AuthProvider = ({ children }) => {
818
const [isAuthenticated, setIsAuthenticated] = useState(false)
919
const [user, setUser] = useState(null)
@@ -12,7 +22,7 @@ export const AuthProvider = ({ children }) => {
1222

1323
useEffect(() => {
1424
const controller = new AbortController()
15-
25+
1626
const checkAuth = async () => {
1727
try {
1828
const userData = await api.me();
@@ -28,17 +38,17 @@ export const AuthProvider = ({ children }) => {
2838
// but for the purpose of UI state, we treat them as not authenticated.
2939
// However, if it's a 401, the api client might have already cleared it or we should ensure it's cleared.
3040
if (err?.status === 401) {
31-
api.setToken(null);
41+
api.setToken(null);
3242
}
3343
} finally {
3444
if (!controller.signal.aborted) {
3545
setLoading(false)
3646
}
3747
}
3848
}
39-
49+
4050
checkAuth()
41-
51+
4252
return () => {
4353
controller.abort()
4454
}
@@ -48,27 +58,27 @@ export const AuthProvider = ({ children }) => {
4858
try {
4959
setError(null)
5060
setLoading(true)
51-
61+
5262
const sanitizedUsername = username.trim()
5363
const response = await api.login(sanitizedUsername, password)
54-
64+
5565
if (!response?.user) {
5666
throw new Error('Ungueltige Antwort vom Server')
5767
}
58-
68+
5969
api.setToken(response.token ?? null)
6070
setIsAuthenticated(true)
6171
setUser(response.user)
62-
72+
6373
return { success: true }
6474
} catch (err) {
6575
api.setToken(null)
6676
setIsAuthenticated(false)
6777
setUser(null)
68-
78+
6979
const message = err.message || 'Ungueltige Anmeldedaten'
7080
setError(message)
71-
81+
7282
return { success: false, error: message }
7383
} finally {
7484
setLoading(false)
@@ -101,10 +111,10 @@ AuthProvider.propTypes = {
101111

102112
export const useAuth = () => {
103113
const context = useContext(AuthContext)
104-
114+
105115
if (!context) {
106116
throw new Error('useAuth must be used within AuthProvider')
107117
}
108-
118+
109119
return context
110120
}

src/context/TutorialContext.jsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ import { api } from '../api/client'
44
import { getIconComponent as getIconComponentFromMap } from '../utils/iconMap'
55
const TutorialContext = createContext(null)
66

7+
/**
8+
* Global Tutorial Data Provider.
9+
*
10+
* Centralizes the fetching, caching, and management of tutorial content.
11+
*
12+
* Features:
13+
* - **Resilient Fetching**: Implements exponential backoff retry logic (up to 3 attempts) for flaky networks.
14+
* - **CRUD Operations**: Exposes `add`, `update`, and `delete` methods that sync local state with backend.
15+
* - **Abort Safety**: Cancels in-flight requests on unmount to prevent state updates on destroyed components.
16+
*/
717
export const TutorialProvider = ({ children }) => {
818
const [tutorials, setTutorials] = useState([])
919
const [loading, setLoading] = useState(true)

src/pages/Home.jsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import { useLocation, useNavigate } from 'react-router-dom'
33
import Hero from '../components/Hero'
44
import TutorialSection from '../components/TutorialSection'
55
import { scrollToSection } from '../utils/scrollToSection'
6+
7+
/**
8+
* The application's homepage component.
9+
*
10+
* Acts as the primary landing point for authenticated or returning users.
11+
*
12+
* Features:
13+
* - Smart Scroll Restoration: Handles `location.state.scrollTo` to jump to specific sections upon navigation.
14+
* - Composition: Combines the `Hero` (standard) and `TutorialSection` components.
15+
*/
616
const Home = () => {
717
const location = useLocation()
818
const navigate = useNavigate()

src/pages/PostDetail.jsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,7 @@ import { Calendar, Clock, User, ArrowLeft, Share2, Bookmark } from 'lucide-react
1414
* - Table of Contents (ToC): Implements a Scroll-Spy to highlight current section.
1515
* - Dynamic SEO: Injects post-specific meta tags for social sharing and search.
1616
* - Advanced Meta: Calculates "Read Time" if not provided by backend.
17-
*/
18-
/**
19-
* Detailed Tutorial Viewer.
20-
*
21-
* Features:
22-
* - Hybrid Loading: Attempts to use cached context data before fetching from API.
23-
* - Modular Rendering: Decouples header, topics, and content for scalability.
24-
* - Nav Logic: Intelligent "Go Back" that stays within app history when possible.
17+
* - Interaction: Integrated comments section and share functionality.
2518
*/
2619
const PostDetail = () => {
2720
const { pageSlug, postSlug } = useParams()
@@ -73,6 +66,8 @@ const PostDetail = () => {
7366
7467
if (!post) return <div className="min-h-screen flex items-center justify-center text-slate-400">Loading...</div>
7568
69+
const readTimeDisplay = post.readTime || `${ Math.ceil((post.content_markdown?.length || 0) / 1000) + 1 } min read`
70+
7671
return (
7772
<div className="min-h-screen bg-slate-950 pt-24 pb-20">
7873
<Helmet>
@@ -115,7 +110,7 @@ const PostDetail = () => {
115110
</div>
116111
<div className="flex items-center gap-2">
117112
<Clock className="w-4 h-4" />
118-
<span>{post.readTime || `${ Math.ceil((post.content_markdown?.length || 0) / 1000) + 1 } min read`}</span>
113+
<span>{readTimeDisplay}</span>
119114
</div>
120115
</div>
121116
</div>

src/utils/pwa.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
// Global variable to store the install prompt event
22
let deferredPrompt = null;
3+
4+
/**
5+
* Service Worker Registration and PWA Install Logic.
6+
*
7+
* Functions:
8+
* - `registerServiceWorker`: Registers the SW script (`/sw.js`) for offline capabilities.
9+
* - `initPWA`: Listens for the `beforeinstallprompt` event to enable custom install UI.
10+
* - `installPWA`: Triggers the browser's native install prompt when the user clicks the install button.
11+
* - `isPWAInstalled`: Checks if the app is running in standalone mode (installed).
12+
*/
313
export const registerServiceWorker = async () => {
414
if ('serviceWorker' in navigator) {
515
try {

src/utils/remarkMergeInlineParagraphs.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11

2-
2+
/**
3+
* Remark Plugin: Merge Inline Paragraphs (Stub).
4+
*
5+
* Placeholder plugin structure for future implementation of AST transformations.
6+
* Currently performs no-op traversals.
7+
*/
38
const tightenListItems = () => { }
49
const reattachDanglingParagraphs = () => { }
510
const mergeLooseParagraphs = () => { }

src/utils/sanitize.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
import DOMPurify from 'dompurify';
2+
3+
/**
4+
* HTML Sanitization Utilities.
5+
*
6+
* Wraps DOMPurify to provide strict, configured sanitization for HTML content.
7+
* Prevents XSS attacks by stripping dangerous tags and attributes.
8+
*/
29
const DEFAULT_CONFIG = {
310
ALLOWED_TAGS: [
411
'p', 'br', 'strong', 'em', 'u', 's', 'a', 'ul', 'ol', 'li',

0 commit comments

Comments
 (0)