diff --git a/static/script.js b/static/script.js
index aebc922..2bfc262 100644
--- a/static/script.js
+++ b/static/script.js
@@ -350,9 +350,10 @@ if (isIndexPage) {
}
function syncSkillsHiddenInput() {
- // Keep the hidden in sync for form serialisation
- // The API expects a comma-separated string, so join the array that way
- skillsHidden.value = selectedSkills.join(", ");
+ // Keep the hidden in sync for form serialisation.
+ // JSON.stringify preserves skill names that contain commas (e.g. "HTML, CSS")
+ // so the backend can reconstruct the exact array without mis-splitting.
+ skillsHidden.value = JSON.stringify(selectedSkills);
}
updateQuickPickState();
diff --git a/utils/recommender.py b/utils/recommender.py
index 308c14f..d8cdf84 100644
--- a/utils/recommender.py
+++ b/utils/recommender.py
@@ -32,18 +32,32 @@
def parse_skills(skills_string):
"""
- Convert a raw comma-separated skills string into
- a normalized lowercase list.
+ Convert a skills string into a normalized lowercase list.
- Example:
- "JS, HTML5, CSS3" -> ["javascript", "html", "css"]
- """
+ Accepts two formats:
+ 1. JSON array (preferred): '["HTML, CSS", "JavaScript"]'
+ Handles skill names that contain commas without mis-splitting.
+ 2. Comma-separated string (legacy fallback): "HTML, CSS, JavaScript"
- raw_skills = [
- s.strip().lower()
- for s in skills_string.split(",")
- if s.strip()
- ]
+ Example:
+ '["JS", "HTML5", "CSS3"]' -> ["javascript", "html", "css"]
+ """
+ import json
+
+ try:
+ # Preferred path: frontend sends a JSON-serialized array
+ parsed = json.loads(skills_string)
+ if isinstance(parsed, list):
+ raw_skills = [s.strip().lower() for s in parsed if isinstance(s, str) and s.strip()]
+ else:
+ raise ValueError("Parsed JSON is not a list")
+ except (json.JSONDecodeError, ValueError, TypeError):
+ # Fallback: handle plain comma-separated strings
+ raw_skills = [
+ s.strip().lower()
+ for s in skills_string.split(",")
+ if s.strip()
+ ]
normalized_skills = [
SKILL_ALIASES.get(skill, skill)