-
Notifications
You must be signed in to change notification settings - Fork 0
New stats page fm #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| return sections.includes(pageSection); | ||
| }) | ||
| // Filter by difficulty if specified | ||
| .filter((page) => { | ||
| return ( | ||
| selectedDifficulty === 'all' || page.difficulty === selectedDifficulty | ||
| ); | ||
| }) | ||
| // Optionally filter out outdated content | ||
| .filter((page) => { | ||
| if (includeOutdated) return true; | ||
| const lastUpdated = new Date(page.lastUpdated); | ||
| const sixMonthsAgo = new Date(); | ||
| sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6); | ||
| return lastUpdated > sixMonthsAgo; | ||
| }) | ||
| // Apply metric thresholds | ||
| .filter((page) => { | ||
| if (minimumViews > 0 && page.views < minimumViews) return false; | ||
| if ( | ||
| minimumCompletionRate > 0 && | ||
| page.completionRate < minimumCompletionRate / 100 | ||
| ) | ||
| return false; | ||
| return true; | ||
| }) | ||
| // Compute an engagement score for demonstration purposes | ||
| .map((page) => { | ||
| const viewsNorm = Math.min(page.views / 250000, 1); | ||
| const completionNorm = page.completionRate; | ||
| const timeSpentNorm = Math.min(page.avgTimeSpent / 600, 1); | ||
|
|
||
| // Freshness factor computed from last update date | ||
| const lastUpdated = new Date(page.lastUpdated); | ||
| const monthsOld = (new Date() - lastUpdated) / (30 * 24 * 60 * 60 * 1000); | ||
| const freshnessScore = Math.max(0, 1 - monthsOld / 24); | ||
|
|
||
| // Difficulty multiplier for weighting | ||
| let difficultyMultiplier = 1; | ||
| if (page.difficulty === 'beginner') difficultyMultiplier = 0.9; | ||
| if (page.difficulty === 'intermediate') difficultyMultiplier = 1.0; | ||
| if (page.difficulty === 'advanced') difficultyMultiplier = 1.2; | ||
|
|
||
| // Compute weighted engagement score | ||
| const engagementScore = | ||
| (0.4 * viewsNorm + | ||
| 0.3 * completionNorm + | ||
| 0.2 * timeSpentNorm + | ||
| 0.1 * Math.pow(freshnessScore, 2)) * | ||
| difficultyMultiplier; | ||
|
|
||
| return { | ||
| ...page, | ||
| engagementScore, | ||
| }; | ||
| }) | ||
| // Sort the computed results | ||
| .sort((a, b) => { | ||
| let comparison = 0; | ||
| if (selectedSortMethod === 'views') { | ||
| comparison = b.views - a.views; | ||
| } else if (selectedSortMethod === 'completion') { | ||
| comparison = b.completionRate - a.completionRate; | ||
| } else if (selectedSortMethod === 'timeSpent') { | ||
| comparison = b.avgTimeSpent - a.avgTimeSpent; | ||
| } else if (selectedSortMethod === 'engagement') { | ||
| comparison = b.engagementScore - a.engagementScore; | ||
| } else if (selectedSortMethod === 'lastUpdated') { | ||
| comparison = new Date(b.lastUpdated) - new Date(a.lastUpdated); | ||
| } | ||
| return sortOrder === 'desc' ? comparison : -comparison; | ||
| }) | ||
| // Limit the results to displayLimit entries. | ||
| .slice(0, displayLimit); | ||
|
|
||
| const filterControls = ( | ||
| <div | ||
| className="filter-controls" | ||
| style={{marginBottom: '20px', display: 'flex', gap: '15px'}}> | ||
| <div> | ||
| <select | ||
| value={selectedDifficulty} | ||
| onChange={(e) => setSelectedDifficulty(e.target.value)} | ||
| style={{ | ||
| padding: '8px 12px', | ||
| borderRadius: '6px', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The processedData variable is computed on every render. This involves multiple array iterations (filter, map, sort) and potentially complex calculations (date manipulation, math operations) for each item in pageViewData. Recalculating this data on every render can lead to performance degradation, impacting FCP/LCP and user experience, especially if pageViewData were larger or component re-renders frequently.
To optimize, memoize the processedData computation using React.useMemo. This ensures the data is re-calculated only when its dependencies change.
Review by Conductor
| style={{height: '3px', width: '50%'}} | ||
| /> | ||
| </div> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The <img> element for /images/meta-gradient.png is missing width and height attributes. While CSS styles are applied for width and height, providing these attributes directly on the <img> tag helps the browser reserve space for the image before it loads, preventing layout shifts (CLS). Additionally, an alt attribute should be provided for accessibility; use alt="" for decorative images.
Specify width and height attributes that reflect the image's intrinsic aspect ratio, and add an appropriate alt attribute.
Review by Conductor
No description provided.