Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 49 additions & 44 deletions static/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -478,22 +478,20 @@ if (clearFiltersBtn) {
// ----------------------------------------------------------

form.addEventListener("submit", function (evt) {
evt.preventDefault(); //stop the browser from reloading the page on form submit
clearAllErrors()
evt.preventDefault();
clearAllErrors();

if (skillsTextInput.value.trim()) {
addSkill(skillsTextInput.value);
skillsTextInput.value = "";
hideSuggestions();
}

if (!validateForm()) return; //stop - anything missing/invalid
if (!validateForm()) return;

setLoadingState(true);

// Allow browser to paint spinner before request starts
requestAnimationFrame(function () {

var payload = {
skills: skillsHidden.value.trim() || skillsTextInput.value.trim(),
level: document.getElementById("level").value,
Expand All @@ -506,57 +504,44 @@ if (clearFiltersBtn) {
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
})
.then(function (res) {
return res.json();
})
.then(function (res) { return res.json(); })
.then(function (data) {

setLoadingState(false);

if (data.error) {
var generalErr = document.getElementById("form-error-general");

if (generalErr) {
generalErr.textContent = data.error;
}

if (generalErr) generalErr.textContent = data.error;
return;
}

renderResults(data.projects || [], data.message);
})
.catch(function () {

setLoadingState(false);
//combine form values into an object to send to server/api
var payload = {
// Prefer the hidden input value; fall back to raw text box if hidden input is empty
skills: skillsHidden.value.trim() || skillsTextInput.value.trim(),
level: document.getElementById("level").value,
interest: document.getElementById("interest").value,
time: document.getElementById("time").value
};
var generalErr = document.getElementById("form-error-general");
if (generalErr) {
generalErr.textContent = "Something went wrong. Please try again.";
}
});
});
});

// Manages the loading state of the form and results section(whats visible or not)
// Manages the loading state of the form and results section
function setLoadingState(isLoading) {
// Disable the button so the user can't accidentally submit twice
submitBtn.disabled = isLoading;
submitBtn.setAttribute("aria-busy", isLoading);
btnLabel.style.display = isLoading ? "none" : "inline";
btnLoading.style.display = isLoading ? "inline-flex" : "none";

if (isLoading) {
// Show the results section with only the loading indicator visible
resultsSection.style.display = "block";
resultsLoadingEl.style.display = "block";
resultsGrid.style.display = "none";
resultsEmptyEl.style.display = "none";
// Scroll down so the user can see the spinner without manually scrolling
resultsSection.scrollIntoView({ behavior: "smooth" });
} else {
resultsLoadingEl.style.display = "none";
resultsGrid.style.display = "grid"; //switch back to gird layout
resultsLoadingEl.style.display = "none";
resultsGrid.style.display = "grid";
}
}

Expand All @@ -565,25 +550,15 @@ if (clearFiltersBtn) {
// Render result cards
// ----------------------------------------------------------

//takes the array of projects from the api and draws them on the page as cards
//if array is empty it shows the "no results" message instead
function renderResults(projects, message) {
resultsSection.style.display = "block";
resultsLoadingEl.style.display = "none";
// Clear out any cards from a previous search before showing new ones
resultsGrid.innerHTML = "";

if (!projects || projects.length === 0) {
resultsGrid.style.display = "none";
resultsEmptyEl.style.display = "block";
resultsGrid.style.display = "none";
resultsEmptyEl.style.display = "block";
if (message && emptyMessageEl) emptyMessageEl.textContent = message;
if (!projects || projects.length === 0) { //if no projects returned from api, show the "no results" message and hide the grid
resultsGrid.style.display = "none";
resultsEmptyEl.style.display = "block";

// Show a friendly custom message when the user selected an interest
var selectedInterest = document.getElementById("interest")?.value;
if (selectedInterest) {
emptyMessageEl.textContent = "No projects are currently available for this interest. Please check back later or try a different area.";
Expand All @@ -600,7 +575,6 @@ if (clearFiltersBtn) {
resultsEmptyEl.style.display = "none";
resultsGrid.style.display = "grid";

//build a card for each project and add it to the grid
projects.forEach(function (project) {
resultsGrid.appendChild(buildProjectCard(project));
});
Expand Down Expand Up @@ -681,6 +655,30 @@ if (clearFiltersBtn) {
} // end isIndexPage


// ============================================================
// Code viewer helpers (detail page)
// ============================================================
function renderCodeWithLineNumbers(code) {
var lines = (code || "").split("\n");
return lines.map(function (line, index) {
var row = document.createElement("div");
row.className = "code-line";

var lineNum = document.createElement("span");
lineNum.className = "line-number";
lineNum.textContent = String(index + 1);

var lineContent = document.createElement("span");
lineContent.className = "line-content";
lineContent.textContent = line;

row.appendChild(lineNum);
row.appendChild(lineContent);
return row;
});
}


// ============================================================
// DETAIL PAGE
// ============================================================
Expand Down Expand Up @@ -726,7 +724,12 @@ if (isDetailPage) {
if (codeContentEl) codeContentEl.textContent = "Loading starter code...";

fetch("/project/" + PROJECT_ID + "/code")
.then(function (res) { return res.json(); })
.then(function (res) {
return res.json().then(function (data) {
if (!res.ok) throw new Error(data.error || "Failed to load starter code.");
return data;
});
})
.then(function (data) {
if (data.error) {
if (codeContentEl) codeContentEl.textContent = "Error: " + data.error;
Expand All @@ -742,9 +745,11 @@ if (isDetailPage) {
// Mark as fetched so we don't hit the API again on the next open
codeFetched = true;
})
.catch(function () {
.catch(function (err) {
if (codeContentEl) {
codeContentEl.textContent = "Could not load starter code. Try downloading it instead.";
codeContentEl.textContent = err && err.message
? "Error: " + err.message
: "Could not load starter code. Try downloading it instead.";
}
});
}
Expand Down
31 changes: 19 additions & 12 deletions static/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -2374,12 +2374,13 @@ select:focus {
text-decoration: none;
}

/* ---- Code Panel (slide-up) -------------------------------- */
/* ---- Code Panel (centered popup modal) -------------------- */
.code-panel-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(10, 15, 80, 0.6);
background: rgba(10, 15, 80, 0.65);
backdrop-filter: blur(4px);
z-index: 300;
}

Expand All @@ -2389,22 +2390,27 @@ select:focus {

.code-panel {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 72vh;
top: 50%;
left: 50%;
width: min(920px, calc(100vw - 32px));
height: min(80vh, 720px);
background: #0d1117;
border-radius: var(--r-lg) var(--r-lg) 0 0;
box-shadow: 0 -8px 48px rgba(0, 0, 0, 0.45);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: var(--r-lg);
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.55);
z-index: 301;
display: flex;
flex-direction: column;
transform: translateY(100%);
transition: transform 0.3s ease;
transform: translate(-50%, -50%) scale(0.94);
opacity: 0;
pointer-events: none;
transition: transform 0.25s ease, opacity 0.25s ease;
}

.code-panel.active {
transform: translateY(0);
transform: translate(-50%, -50%) scale(1);
opacity: 1;
pointer-events: auto;
}

.code-panel-header {
Expand Down Expand Up @@ -2680,7 +2686,8 @@ select:focus {
}

.code-panel {
height: 82vh;
width: calc(100vw - 24px);
height: min(88vh, 720px);
}

.footer-inner {
Expand Down
10 changes: 5 additions & 5 deletions templates/project.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ <h1 class="detail-title">{{ project.title }}</h1>
</svg>
Download Starter Code
</a>
<button class="btn-view-code" id="btn-view-code" aria-label="Open inline code viewer">
<button type="button" class="btn-view-code" id="btn-view-code" aria-label="Open starter code popup">
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="16 18 22 12 16 6" />
Expand Down Expand Up @@ -259,7 +259,7 @@ <h3 class="sidebar-card-title">
</h3>
<p class="sidebar-card-desc">A working template to kick off this project without starting from scratch.</p>
<div class="sidebar-code-actions">
<button class="btn-view-code-sm" id="btn-view-code-sm">View Code</button>
<button type="button" class="btn-view-code-sm" id="btn-view-code-sm">View Code</button>
<a href="/project/{{ project.id }}/download" class="btn-download-sm">Download</a>
</div>
</div>
Expand All @@ -270,9 +270,9 @@ <h3 class="sidebar-card-title">
</div>

<!-- ============================================================
Starter Code Viewer Panel (slides in from bottom)
Starter Code Viewer Popup
============================================================ -->
<div class="code-panel" id="code-panel">
<div class="code-panel" id="code-panel" role="dialog" aria-modal="true" aria-labelledby="code-panel-filename">
<div class="code-panel-header">
<div class="code-panel-title">
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
Expand All @@ -296,7 +296,7 @@ <h3 class="sidebar-card-title">
</svg>
<span class="copy-btn-label">Copy Code</span>
</button>
<button class="code-panel-close" id="code-panel-close">Close</button>
<button type="button" class="code-panel-close" id="code-panel-close">Close</button>
</div>
</div>
<!-- Code is injected here by script.js -->
Expand Down