-
Notifications
You must be signed in to change notification settings - Fork 0
New stats page fmartin #60
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
| 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', | ||
| border: '1px solid #ccc', | ||
| backgroundColor: '#f7f7f7', | ||
| color: '#333', | ||
| }}> | ||
| <option value="all">All Difficulties</option> | ||
| <option value="beginner">Beginner</option> | ||
| <option value="intermediate">Intermediate</option> | ||
| <option value="advanced">Advanced</option> | ||
| </select> | ||
| </div> | ||
|
|
||
| <div> | ||
| <select | ||
| value={selectedSortMethod} | ||
| onChange={(e) => setSelectedSortMethod(e.target.value)} | ||
| style={{ | ||
| padding: '8px 12px', | ||
| borderRadius: '6px', | ||
| border: '1px solid #ccc', | ||
| backgroundColor: '#f7f7f7', | ||
| color: '#333', | ||
| }}> |
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 data processing logic for processedData (filtering, mapping, sorting) executes on every render. For potentially large datasets or frequent re-renders (e.g., state changes from filter controls), this synchronous computation can block the main thread, impacting interactivity and potentially increasing LCP or FCP. Consider memoizing this computation using React.useMemo to ensure it only recalculates when its dependencies change.
Review by Conductor
| /> | ||
| </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.
This <img> tag is missing width and height attributes. Omitting these can cause layout shifts (CLS) during page load as the browser doesn't know the image dimensions initially. Specify width and height attributes to reserve space for the image.
Review by Conductor
|
|
||
| <div style={{textAlign: 'center', marginTop: '20px'}}> | ||
| <img | ||
| src="/images/meta-gradient.png" | ||
| style={{height: '3px', width: '50%'}} | ||
| /> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default DocumentationStatistics; |
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.
This <img> tag is missing width and height attributes. Omitting these can cause layout shifts (CLS) during page load as the browser doesn't know the image dimensions initially. Specify width and height attributes to reserve space for the image.
| <div style={{textAlign: 'center', marginTop: '20px'}}> | |
| <img | |
| src="/images/meta-gradient.png" | |
| style={{height: '3px', width: '50%'}} | |
| /> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default DocumentationStatistics; | |
| <img | |
| src="/images/meta-gradient.png" | |
| width="1000" // Example width, adjust as needed | |
| height="6" // Example height (adjust based on actual image aspect ratio) | |
| alt="" // Add descriptive alt text if the image conveys meaning, otherwise leave empty for decorative images. | |
| style={{height: '3px', width: '50%'}} | |
| /> |
Review by Conductor
No description provided.