From ff413db68cb854aae045c5a9022b0689fbd6bb8a Mon Sep 17 00:00:00 2001 From: Ansh Date: Thu, 28 May 2026 11:07:36 +0530 Subject: [PATCH 1/2] Fix issue with skill-based project search and improve skills selection UI --- .vscode/settings.json | 3 ++ routes/main_routes.py | 11 ++++++- static/script.js | 65 ++++++++++++++++++++++++++---------- static/style.css | 76 +++++++++++++++++++++++++++++++++++++++++++ templates/index.html | 45 +++++++++++++------------ 5 files changed, 162 insertions(+), 38 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..cc081eec --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python-envs.defaultEnvManager": "ms-python.python:venv" +} \ No newline at end of file diff --git a/routes/main_routes.py b/routes/main_routes.py index 658553ef..9121e720 100644 --- a/routes/main_routes.py +++ b/routes/main_routes.py @@ -88,7 +88,16 @@ def recommend(): ) }), 200 - return jsonify({"projects": results}), 200 + # Ensure all projects have IDs in the response + projects_data = [] + for project in results: + project_dict = dict(project) # Convert to dict if needed + # Make sure ID is included + if 'id' not in project_dict: + project_dict['id'] = project.get('id', 0) + projects_data.append(project_dict) + + return jsonify({"projects": projects_data}), 200 @main.route("/project/") diff --git a/static/script.js b/static/script.js index f97e5a0d..895fb978 100644 --- a/static/script.js +++ b/static/script.js @@ -206,6 +206,7 @@ if (clearFiltersBtn) { activeSuggestionIndex = -1; if (suggestionsDiv) { suggestionsDiv.style.display = "none"; + suggestionsDiv.classList.remove("show"); suggestionsDiv.innerHTML = ""; } syncSuggestionsA11yState(); @@ -230,10 +231,17 @@ if (clearFiltersBtn) { items.forEach(function (skill, index) { var item = document.createElement("div"); item.className = "suggestion-item"; + + // Check if skill is already selected for multi-select styling + var isSelected = isSkillSelected(skill); + if (isSelected) { + item.classList.add("selected"); + } + item.textContent = skill; item.setAttribute("role", "option"); item.setAttribute("id", "skills-suggestion-" + index); - item.setAttribute("aria-selected", "false"); + item.setAttribute("aria-selected", isSelected ? "true" : "false"); // Prevent the input blur handler from closing the menu before click runs. item.addEventListener("mousedown", function (evt) { @@ -247,6 +255,11 @@ if (clearFiltersBtn) { item.addEventListener("click", function () { selectSuggestion(skill); + // Keep dropdown open if clicking from dropdown (multi-select mode) + if (suggestionsDiv.classList.contains("show")) { + displaySuggestions(items); + skillsTextInput.focus(); + } }); suggestionsDiv.appendChild(item); @@ -320,6 +333,24 @@ if (clearFiltersBtn) { }); }); + // Multi-select dropdown toggle functionality + var dropdownBtn = document.getElementById("skills-dropdown-toggle"); + if (dropdownBtn) { + dropdownBtn.addEventListener("click", function (e) { + e.preventDefault(); + e.stopPropagation(); + var suggestionsOpen = suggestionsDiv.style.display === "block"; + + if (suggestionsOpen) { + hideSuggestions(); + } else { + // Show all available skills in dropdown + displaySuggestions(availableSkills); + suggestionsDiv.classList.add("show"); + } + }); + } + // Show suggestions on input skillsTextInput.addEventListener("input", function (evt) { var typedValue = evt.target.value.trim(); @@ -510,7 +541,7 @@ if (clearFiltersBtn) { return res.json(); }) .then(function (data) { - + console.log("API Response:", data); setLoadingState(false); if (data.error) { @@ -526,16 +557,13 @@ if (clearFiltersBtn) { 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 = "Network error. Please try again."; + } + }); + }); }); // Manages the loading state of the form and results section(whats visible or not) @@ -568,20 +596,17 @@ if (clearFiltersBtn) { //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) { + console.log("Rendering results with projects:", projects); + console.log("Message:", 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; @@ -614,6 +639,10 @@ if (clearFiltersBtn) { var card = document.createElement("div"); card.className = "project-card"; + // Console logging for debugging + console.log("Building card for project:", project); + console.log("Project ID:", project.id); + // Title var title = document.createElement("h3"); title.className = "project-card-title"; @@ -650,6 +679,8 @@ if (clearFiltersBtn) { link.className = "btn-details"; link.textContent = "View Full Project"; link.href = "/project/" + project.id; //each project has a unique id + + console.log("Created link with href:", link.href); footer.appendChild(link); diff --git a/static/style.css b/static/style.css index b399ee5f..dc447b81 100644 --- a/static/style.css +++ b/static/style.css @@ -1368,6 +1368,82 @@ label { box-shadow: none; } +/* Multi-select wrapper for skills input */ +.skill-multiselect-wrapper { + display: flex; + align-items: center; + flex: 1; + min-width: 140px; + gap: 4px; + position: relative; +} + +.skill-dropdown-btn { + background: none; + border: none; + cursor: pointer; + padding: 4px 6px; + color: var(--gray-500); + display: flex; + align-items: center; + justify-content: center; + transition: color var(--t); + flex-shrink: 0; +} + +.skill-dropdown-btn:hover { + color: var(--indigo-600); +} + +.skill-dropdown-btn svg { + width: 16px; + height: 16px; +} + +/* Multi-select dropdown styling */ +.skills-dropdown.show { + display: block; + animation: slideDown 0.2s ease; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.skills-dropdown .suggestion-item { + display: flex; + align-items: center; + gap: 8px; +} + +.skills-dropdown .suggestion-item::before { + content: ''; + display: inline-block; + width: 16px; + height: 16px; + border: 1.5px solid var(--gray-300); + border-radius: 3px; + flex-shrink: 0; + transition: all 0.15s ease; +} + +.skills-dropdown .suggestion-item.selected::before { + background: var(--indigo-600); + border-color: var(--indigo-600); + box-shadow: inset 0 0 0 2px var(--white); +} + +.skills-dropdown .suggestion-item:hover { + background: var(--indigo-50); +} + /*suggestions dropdown*/ .skills-suggestions { position: absolute; diff --git a/templates/index.html b/templates/index.html index aa7751f8..f74d0043 100644 --- a/templates/index.html +++ b/templates/index.html @@ -26,7 +26,7 @@ Home How It Works Features - Find Project + Find Project GitHub @@ -39,7 +39,7 @@ Home How It Works Features - Find Project + Find Project GitHub @@ -78,7 +78,7 @@

@@ -285,7 +285,7 @@

Everything You Need to Start Building

Personalized Matches

Projects are scored against your exact skills, level, and interest — not pulled from a generic list.

- Try it now + Try it now
@@ -296,7 +296,7 @@

Personalized Matches

Step-by-Step Roadmaps

Each project includes a numbered roadmap so you always know what to build next, without guessing.

- Explore roadmaps + Explore roadmaps
@@ -308,7 +308,7 @@

Step-by-Step Roadmaps

Starter Code Included

Download a working template for every project. Skip the blank-page problem and start building immediately.

- Get starter code + Get starter code @@ -318,10 +318,10 @@

Starter Code Included

-
+
Get Your Recommendations
-

Find Your Next Project

+

Find The Project

Fill in your details below and DevPath will match you to the most relevant projects.

@@ -347,16 +347,21 @@

Find Your Next Project

- -
+
+ + +
+
@@ -554,7 +559,7 @@

No Projects Found

Start Building.
A New Skill Awaits.

Find a project that challenges you and grow with every line of code.

- Find My Project + Find My Project
@@ -602,7 +607,7 @@
  • Home
  • How It Works
  • Features
  • -
  • Find Project
  • +
  • Find Project
  • From 9e9865d32d84ae73cfa687741c6bb6cfe37c2909 Mon Sep 17 00:00:00 2001 From: Ansh Date: Fri, 5 Jun 2026 21:49:44 +0530 Subject: [PATCH 2/2] Resolve merge conflicts and add temp file --- .vscode/settings.json | 2 +- tempCodeRunnerFile.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 tempCodeRunnerFile.py diff --git a/.vscode/settings.json b/.vscode/settings.json index cc081eec..c9ebf2d2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "python-envs.defaultEnvManager": "ms-python.python:venv" + "python-envs.defaultEnvManager": "ms-python.python:system" } \ No newline at end of file diff --git a/tempCodeRunnerFile.py b/tempCodeRunnerFile.py new file mode 100644 index 00000000..7a0b7f00 --- /dev/null +++ b/tempCodeRunnerFile.py @@ -0,0 +1 @@ +app \ No newline at end of file