Skip to content

Commit 845b9b3

Browse files
committed
merge: resolve conflicts for PR #39 and migrate to .tsx
2 parents a8669e5 + b93c188 commit 845b9b3

File tree

101 files changed

+10362
-4265
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+10362
-4265
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Lesson Feedback
2+
description: Share feedback, report issues, or suggest improvements for a lesson
3+
title: "Lesson Feedback: "
4+
labels:
5+
- lesson-feedback
6+
- education
7+
- needs-triage
8+
9+
body:
10+
- type: input
11+
id: lesson-name
12+
attributes:
13+
label: Lesson name
14+
description: Name of the lesson you are providing feedback for
15+
placeholder: e.g. Introduction to Open Source
16+
validations:
17+
required: true
18+
19+
- type: dropdown
20+
id: feedback-type
21+
attributes:
22+
label: Feedback type
23+
description: What kind of feedback are you providing?
24+
options:
25+
- Bug / Error in content
26+
- Suggestion / Improvement
27+
- Quality / Clarity feedback
28+
validations:
29+
required: true
30+
31+
- type: textarea
32+
id: description
33+
attributes:
34+
label: Feedback description
35+
description: >
36+
Please describe your feedback clearly.
37+
If reporting a bug, mention the section, heading, or link.
38+
placeholder: Write your feedback here...
39+
validations:
40+
required: true

.github/REFERENCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ Full spec: https://bioschemas.org/profiles/TrainingMaterial/1.0-RELEASE
277277

278278
```
279279
┌─────────────────────────────────────────────┐
280-
│ Google Sheets (via getSheetData.js) │
280+
│ Google Sheets (via getSheetData.ts) │
281281
│ - 56 lessons │
282282
│ - 38 columns │
283283
└────────────────┬────────────────────────────┘

.github/SKILL_BADGES_IMPLEMENTATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Redesigned lesson card component matching student Figma design:
6767
- SubTopic groupings with uppercase headers (matching "LEARN THE BASICS" style)
6868
- Consistent visual design across all pathway pages
6969

70-
#### 2. `src/components/LessonFilter.jsx`
70+
#### 2. `src/components/LessonFilter.tsx`
7171

7272
**Changes**:
7373
- Imported `LessonCard` component

.github/scripts/README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# PR Validation Scripts
2+
3+
These scripts are used by the `pr-check.yml` workflow to validate PRs before merging.
4+
5+
## Scripts
6+
7+
### `validate-data.mjs`
8+
Validates the Google Sheets data source that powers the lessons.
9+
10+
**Checks:**
11+
- CSV is accessible and fetchable
12+
- CSV parses correctly
13+
- At least some lessons have required fields (name, description, url)
14+
- URL formats are valid
15+
16+
**Exit codes:**
17+
- 0: Success (CSV accessible, data usable)
18+
- 1: Failure (CSV unreachable, no valid lessons)
19+
20+
**Usage:**
21+
```bash
22+
node .github/scripts/validate-data.mjs
23+
```
24+
25+
### `validate-build.mjs`
26+
Validates the build output to ensure critical pages and assets exist.
27+
28+
**Checks:**
29+
- dist/ directory exists
30+
- Critical pages exist (index, lessons, pathways, etc.)
31+
- Critical assets exist (CSS, logo)
32+
- Reasonable number of HTML pages generated
33+
- Files are not suspiciously small (empty or error pages)
34+
35+
**Exit codes:**
36+
- 0: Success
37+
- 1: Failure (missing critical files)
38+
39+
**Usage:**
40+
```bash
41+
npm run build
42+
node .github/scripts/validate-build.mjs
43+
```
44+
45+
### `check-links.mjs`
46+
Checks for broken internal links in the built site.
47+
48+
**Checks:**
49+
- All internal href links resolve to real files
50+
- Handles relative and absolute paths
51+
- Handles directory index.html files
52+
53+
**Exit codes:**
54+
- 0: Success (all links valid)
55+
- 1: Failure (broken links found)
56+
57+
**Usage:**
58+
```bash
59+
npm run build
60+
node .github/scripts/check-links.mjs
61+
```
62+
63+
## Running All Checks Locally
64+
65+
To run all PR checks locally before pushing:
66+
67+
```bash
68+
# 1. Validate data source
69+
node .github/scripts/validate-data.mjs
70+
71+
# 2. Type check
72+
npx astro check
73+
74+
# 3. Build
75+
npm run build
76+
77+
# 4. Validate build
78+
node .github/scripts/validate-build.mjs
79+
80+
# 5. Check links
81+
node .github/scripts/check-links.mjs
82+
```
83+
84+
## Adding New Checks
85+
86+
When adding new validation scripts:
87+
88+
1. Create the script in `.github/scripts/`
89+
2. Make it executable: `chmod +x .github/scripts/your-script.mjs`
90+
3. Add it to `.github/workflows/pr-check.yml`
91+
4. Document it in this README
92+
5. Test locally before committing

.github/scripts/check-links.mjs

100644100755
Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,172 @@
1-
console.log("check-links.mjs: skipped");
2-
process.exit(0);
1+
#!/usr/bin/env node
2+
// Link validation script for PR checks
3+
// Checks for broken internal links in the built site
4+
5+
import fs from 'fs';
6+
import path from 'path';
7+
import { fileURLToPath } from 'url';
8+
9+
const __filename = fileURLToPath(import.meta.url);
10+
const __dirname = path.dirname(__filename);
11+
12+
const DIST_DIR = path.resolve(process.cwd(), 'dist');
13+
14+
// Collect all HTML files
15+
function collectHTMLFiles(dir, files = []) {
16+
const entries = fs.readdirSync(dir, { withFileTypes: true });
17+
18+
for (const entry of entries) {
19+
const fullPath = path.join(dir, entry.name);
20+
if (entry.isDirectory()) {
21+
collectHTMLFiles(fullPath, files);
22+
} else if (entry.isFile() && entry.name.endsWith('.html')) {
23+
files.push(fullPath);
24+
}
25+
}
26+
27+
return files;
28+
}
29+
30+
// Extract internal links from HTML
31+
function extractInternalLinks(html, filePath) {
32+
const links = [];
33+
34+
// Match href attributes
35+
const hrefRegex = /href=["']([^"']+)["']/g;
36+
let match;
37+
38+
while ((match = hrefRegex.exec(html)) !== null) {
39+
const href = match[1];
40+
41+
// Skip external links, anchors, mailto, tel, etc.
42+
if (href.startsWith('http://') ||
43+
href.startsWith('https://') ||
44+
href.startsWith('mailto:') ||
45+
href.startsWith('tel:') ||
46+
href.startsWith('#')) {
47+
continue;
48+
}
49+
50+
links.push({ href, file: filePath });
51+
}
52+
53+
return links;
54+
}
55+
56+
// Check if a link resolves to a real file
57+
function checkLink(link, baseDir) {
58+
let targetPath = link.href;
59+
60+
// Remove query strings and anchors
61+
targetPath = targetPath.split('?')[0].split('#')[0];
62+
63+
// Handle absolute paths
64+
if (targetPath.startsWith('/')) {
65+
targetPath = path.join(baseDir, targetPath);
66+
} else {
67+
// Handle relative paths
68+
const linkDir = path.dirname(link.file);
69+
targetPath = path.join(linkDir, targetPath);
70+
}
71+
72+
// Normalize path
73+
targetPath = path.normalize(targetPath);
74+
75+
// Check if file exists
76+
if (fs.existsSync(targetPath)) {
77+
return { valid: true };
78+
}
79+
80+
// Check with .html extension
81+
if (fs.existsSync(targetPath + '.html')) {
82+
return { valid: true };
83+
}
84+
85+
// Check if it's a directory with index.html
86+
if (fs.existsSync(path.join(targetPath, 'index.html'))) {
87+
return { valid: true };
88+
}
89+
90+
return {
91+
valid: false,
92+
error: `Link points to non-existent path: ${targetPath}`
93+
};
94+
}
95+
96+
async function main() {
97+
console.log('🔗 Checking internal links...\n');
98+
99+
if (!fs.existsSync(DIST_DIR)) {
100+
console.error('❌ dist/ directory not found. Build the site first.');
101+
process.exit(1);
102+
}
103+
104+
// Collect all HTML files
105+
const htmlFiles = collectHTMLFiles(DIST_DIR);
106+
console.log(`Found ${htmlFiles.length} HTML files\n`);
107+
108+
if (htmlFiles.length === 0) {
109+
console.error('❌ No HTML files found in dist/');
110+
process.exit(1);
111+
}
112+
113+
// Extract and check all links
114+
const allLinks = [];
115+
const brokenLinks = [];
116+
117+
for (const file of htmlFiles) {
118+
const html = fs.readFileSync(file, 'utf-8');
119+
const links = extractInternalLinks(html, file);
120+
allLinks.push(...links);
121+
}
122+
123+
console.log(`Checking ${allLinks.length} internal links...\n`);
124+
125+
// Check each unique link
126+
const checkedLinks = new Set();
127+
128+
for (const link of allLinks) {
129+
const linkKey = `${link.file}::${link.href}`;
130+
131+
// Skip if already checked
132+
if (checkedLinks.has(linkKey)) {
133+
continue;
134+
}
135+
checkedLinks.add(linkKey);
136+
137+
const result = checkLink(link, DIST_DIR);
138+
139+
if (!result.valid) {
140+
brokenLinks.push({
141+
file: path.relative(DIST_DIR, link.file),
142+
href: link.href,
143+
error: result.error
144+
});
145+
}
146+
}
147+
148+
// Report results
149+
if (brokenLinks.length > 0) {
150+
console.error(`❌ Found ${brokenLinks.length} broken internal links:\n`);
151+
152+
// Group by file
153+
const byFile = {};
154+
brokenLinks.forEach(link => {
155+
if (!byFile[link.file]) byFile[link.file] = [];
156+
byFile[link.file].push(link.href);
157+
});
158+
159+
Object.entries(byFile).forEach(([file, links]) => {
160+
console.error(`📄 ${file}`);
161+
links.forEach(link => console.error(` ❌ ${link}`));
162+
console.error('');
163+
});
164+
165+
process.exit(1);
166+
} else {
167+
console.log(`✅ All ${checkedLinks.size} internal links are valid!`);
168+
process.exit(0);
169+
}
170+
}
171+
172+
main();

0 commit comments

Comments
 (0)