@@ -194,9 +194,48 @@ function recordSearch() {
194194
195195 function normalizeSkill ( skill ) { return skill . trim ( ) . toLowerCase ( ) ; }
196196
197- function isSkillSelected ( skill ) {
198- var normalizedSkill = normalizeSkill ( skill ) ;
199- return selectedSkills . some ( function ( s ) { return normalizeSkill ( s ) === normalizedSkill ; } ) ;
197+ loadProgressState ( ) ;
198+ updateProfileWidgets ( ) ;
199+
200+ ( function initIndexPage ( ) {
201+ var form = document . getElementById ( "recommend-form" ) ;
202+ if ( ! form ) return ;
203+
204+ var submitBtn = document . getElementById ( "submit-btn" ) ;
205+ var btnLabel = document . getElementById ( "btn-label" ) ;
206+ var btnLoading = document . getElementById ( "btn-loading" ) ;
207+ var resultsSection = document . getElementById ( "results-section" ) ;
208+ var resultsGrid = document . getElementById ( "results-grid" ) ;
209+ var resultsLoadingEl = document . getElementById ( "results-loading" ) ;
210+ var resultsEmptyEl = document . getElementById ( "results-empty" ) ;
211+ var emptyMessageEl = document . getElementById ( "empty-message" ) ;
212+ var skillsHidden = document . getElementById ( "skills" ) ;
213+ var skillsInput = document . getElementById ( "skills-input" ) ;
214+ var selectedChips = document . getElementById ( "skill-chips-selected" ) ;
215+ var suggestions = document . getElementById ( "skills-suggestions" ) ;
216+ var skillWrap = document . getElementById ( "skill-input-wrap" ) ;
217+ var quickPickChips = Array . prototype . slice . call ( document . querySelectorAll ( ".skill-chip" ) ) ;
218+ var selectedSkills = [ ] ;
219+ var availableSkills = ( typeof skills !== "undefined" && Array . isArray ( skills ) )
220+ ? skills . map ( function ( item ) { return item . label ; } ) . filter ( Boolean )
221+ : quickPickChips . map ( function ( chip ) { return chip . getAttribute ( "data-skill" ) ; } ) ;
222+ var activeSuggestionIndex = - 1 ;
223+ var visibleSuggestions = [ ] ;
224+ var SAVED_PROJECTS_KEY = "devpathSavedProjects" ;
225+
226+ window . addSkill = function addSkill ( rawSkill ) {
227+ var skill = canonicalSkill ( rawSkill ) ;
228+ if ( ! skill || isSelected ( skill ) ) return ;
229+ selectedSkills . push ( skill ) ;
230+ renderSelectedChips ( ) ;
231+ syncSkillsHiddenInput ( ) ;
232+ updateQuickPickState ( ) ;
233+ clearFieldError ( "skills-error" ) ;
234+ if ( skillsInput ) skillsInput . focus ( ) ;
235+ } ;
236+
237+ function normalize ( value ) {
238+ return String ( value || "" ) . trim ( ) . toLowerCase ( ) ;
200239 }
201240
202241 function getCanonicalSkill ( rawSkill ) {
@@ -327,20 +366,6 @@ function recordSearch() {
327366 if ( skillWrap && ! skillWrap . contains ( evt . target ) ) hideSuggestions ( ) ;
328367 } ) ;
329368
330- //add a skill to the list if it's not empty or a duplicate
331- function addSkill ( rawSkill ) {
332- var skill = getCanonicalSkill ( rawSkill ) ;
333- if ( ! skill ) return ;
334- if ( isSkillSelected ( skill ) ) return ;
335- selectedSkills . push ( skill ) ;
336- renderSelectedChips ( ) ;
337- syncSkillsHiddenInput ( ) ;
338- updateQuickPickState ( ) ;
339- // Once a skill is added, remove the "please add a skill" error if it was showing
340- clearFieldError ( "skills-error" ) ;
341- }
342-
343- // remove a skill from the list and update the UI accordingly
344369 function removeSkill ( skill ) {
345370 // Rebuild the array without the skill that was just removed
346371 selectedSkills = selectedSkills . filter ( function ( s ) { return normalizeSkill ( s ) !== normalizeSkill ( skill ) ; } ) ;
@@ -690,138 +715,7 @@ function recordSearch() {
690715 // Form submission and API call
691716 // ----------------------------------------------------------
692717
693- form . addEventListener ( "submit" , function ( evt ) {
694- evt . preventDefault ( ) ; //stop the browser from reloading the page on form submit
695- clearAllErrors ( )
696-
697- clearAllErrors ( ) ;
698-
699- if ( skillsInput . value . trim ( ) ) {
700- addSkill ( skillsInput . value ) ;
701- skillsInput . value = "" ;
702- hideSuggestions ( ) ;
703- }
704-
705- if ( ! validateForm ( ) ) return ; //stop - anything missing/invalid
706-
707- setLoadingState ( true ) ;
708-
709- // Allow browser to paint spinner before request starts
710- requestAnimationFrame ( function ( ) {
711-
712- var payload = {
713- //combine form values into an object to send to server/api
714- var payload = {
715- skills : skillsHidden . value . trim ( ) || skillsTextInput . value . trim ( ) ,
716- level : document . getElementById ( "level" ) . value ,
717- interest : document . getElementById ( "interest" ) . value ,
718- time : document . getElementById ( "time" ) . value
719- } ;
720-
721- fetch ( "/api/recommend" , {
722- method : "POST" ,
723- headers : { "Content-Type" : "application/json" } ,
724- body : JSON . stringify ( payload )
725- } )
726- . then ( function ( res ) {
727- return res . json ( ) ;
728- } )
729- . then ( function ( data ) {
730- console . log ( "API Response:" , data ) ;
731- setLoadingState ( false ) ;
732- evt . preventDefault ( ) ;
733-
734- clearAllErrors ( ) ;
735-
736- if ( skillsTextInput . value . trim ( ) ) {
737- addSkill ( skillsTextInput . value ) ;
738- skillsTextInput . value = "" ;
739- hideSuggestions ( ) ;
740- }
741-
742- if ( ! validateForm ( ) ) return ;
743-
744- setLoadingState ( true ) ;
745-
746- renderResults ( data . projects || [ ] , data . message ) ;
747- renderResults ( Array . isArray ( data . projects ) ? data . projects : [ ] , data . message ) ;
748- } )
749- . catch ( function ( err ) {
750- setLoadingState ( false ) ;
751- var generalErr = document . getElementById ( "form-error-general" ) ;
752- if ( generalErr ) {
753- generalErr . textContent = "Something went wrong. Please try again." ;
754- }
755- } ) ;
756- } )
757-
758- } ) ;
759-
760- } ;
761-
762- requestAnimationFrame ( function ( ) {
763-
764- var payload = {
765- skills : skillsHidden . value . trim ( ) || skillsTextInput . value . trim ( ) ,
766- level : document . getElementById ( "level" ) . value ,
767- interest : document . getElementById ( "interest" ) . value ,
768- time : document . getElementById ( "time" ) . value
769- } ;
770-
771- fetch ( "/api/recommend" , {
772- method : "POST" ,
773- headers : {
774- "Content-Type" : "application/json"
775- } ,
776- body : JSON . stringify ( payload )
777- } )
778- . then ( function ( res ) {
779- return res . json ( ) ;
780- } )
781- . then ( function ( data ) {
782-
783- setLoadingState ( false ) ;
784-
785- if ( data . error ) {
786- var generalErr = document . getElementById ( "form-error-general" ) ;
787- if ( generalErr ) {
788- generalErr . textContent = "Network error. Please try again." ;
789- }
790- } ) ;
791- } ) ;
792- } ) ;
793- generalErr . textContent = "An unexpected error occurred. Please try again." ;
794- }
795- console . error ( "API request failed:" , err ) ;
796- } ) ;
797- } ) ;
798- } ) ;
799- //post the data to backend api as JSON, then handle the response
800- fetch ( "/api/recommend" , {
801- method : "POST" ,
802- headers : { "Content-Type" : "application/json" } ,
803- body : JSON . stringify ( payload )
804- } )
805- . then ( function ( res ) { return res . json ( ) ; } )
806- . then ( function ( data ) {
807- setLoadingState ( false ) ;
808- if ( data . error ) {
809- var generalErr = document . getElementById ( "form-error-general" ) ;
810- if ( generalErr ) generalErr . textContent = data . error ;
811- return ;
812- }
813- renderResults ( data . projects || [ ] , data . message ) ;
814- } )
815- . catch ( function ( err ) {
816- setLoadingState ( false ) ;
817- var generalErr = document . getElementById ( "form-error-general" ) ;
818- if ( generalErr ) generalErr . textContent = "Something went wrong. Please try again." ;
819- console . error ( err ) ;
820- } ) ;
821- } ) ;
822- } ) ;
823-
824-
718+ // Manages the loading state of the form and results section(whats visible or not)
825719 function setLoadingState ( isLoading ) {
826720 // Disable the button so the user can't accidentally submit twice
827721 submitBtn . disabled = isLoading ;
@@ -887,6 +781,48 @@ function recordSearch() {
887781 return span ;
888782 }
889783
784+ //takes the array of projects from the api and draws them on the page as cards
785+ //if array is empty it shows the "no results" message instead
786+ function renderResults ( projects , message ) {
787+ console . log ( "Rendering results with projects:" , projects ) ;
788+ console . log ( "Message:" , message ) ;
789+
790+ resultsSection . style . display = "block" ;
791+ resultsLoadingEl . style . display = "none" ;
792+ // Clear out any cards from a previous search before showing new ones
793+ resultsGrid . innerHTML = "" ;
794+ recordSearch ( ) ;
795+
796+ if ( ! projects || projects . length === 0 ) {
797+ resultsGrid . style . display = "none" ;
798+ resultsEmptyEl . style . display = "block" ;
799+
800+ // Show a friendly custom message when the user selected an interest
801+ var selectedInterest = document . getElementById ( "interest" ) ?. value ;
802+ if ( selectedInterest ) {
803+ emptyMessageEl . textContent = "No projects are currently available for this interest. Please check back later or try a different area." ;
804+ } else if ( message ) {
805+ emptyMessageEl . textContent = message ;
806+ } else {
807+ emptyMessageEl . textContent = "Try adjusting your skills or choosing a different interest area." ;
808+ }
809+
810+ // Clear out previous results before rendering new ones
811+ resultsGrid . innerHTML = "" ;
812+
813+ // If no projects are returned, show the empty state message
814+ if ( ! projects || projects . length === 0 ) {
815+ resultsGrid . style . display = "none" ;
816+ resultsEmptyEl . style . display = "block" ;
817+
818+ projects . forEach ( function ( project ) {
819+ resultsGrid . appendChild ( buildProjectCard ( project ) ) ;
820+ } ) ;
821+
822+ recordSearch ( ) ;
823+ resultsSection . scrollIntoView ( { behavior : "smooth" } ) ;
824+ }
825+
890826 function buildProjectCard ( project ) {
891827
892828 var card = document . createElement ( "div" ) ;
0 commit comments