@@ -234,8 +234,10 @@ document.addEventListener("alpine:init", () => {
234234 left (subscribed) | middle (catalog search) | right (single-run).
235235 The left column persists to GitHub Secret on demand; the right
236236 column is session-only and cleared after triggering a workflow. */
237- allCourses : [ ] , allCoursesTerms : [ ] , allDepts : [ ] ,
238- subsTerm : "" , subsDept : "" , subsSearchTitle : "" , subsSearchTeacher : "" ,
237+ allCourses : [ ] , allCoursesTerms : [ ] ,
238+ subsTerms : [ ] , subsDepts : [ ] , deptSearchQuery : "" ,
239+ subsSearchTitle : "" , subsSearchTeacher : "" ,
240+ subsTermOpen : false , subsDeptOpen : false ,
239241 subscribedIds : [ ] , singleRunIds : [ ] ,
240242 subsSelLeft : [ ] , subsSelMiddle : [ ] , subsSelRight : [ ] ,
241243 subsFiltered : [ ] ,
@@ -588,19 +590,18 @@ document.addEventListener("alpine:init", () => {
588590
589591 // ── Subscriptions editor (three-column) ──────────────────────────
590592 openSubscriptions ( ) {
591- this . allCourses = ICS . db . getAllCourses ( ) ;
592593 this . allCoursesTerms = ICS . db . getAllCoursesTerms ( ) ;
593- // Derive department list from catalog
594- var deptSet = new Set ( ) ;
595- for ( var i = 0 ; i < this . allCourses . length ; i ++ ) {
596- var d = this . allCourses [ i ] . dept ;
597- if ( d ) deptSet . add ( d ) ;
598- }
599- this . allDepts = Array . from ( deptSet ) . sort ( ) ;
600- this . subsTerm = "" ;
601- this . subsDept = "" ;
594+ // Default: select the first term and load its courses only
595+ // (loading all 20k+ courses at once would freeze the UI).
596+ this . subsTerms = this . allCoursesTerms . length
597+ ? [ this . allCoursesTerms [ 0 ] ] : [ ] ;
598+ this . subsDepts = [ ] ;
599+ this . deptSearchQuery = "" ;
602600 this . subsSearchTitle = "" ;
603601 this . subsSearchTeacher = "" ;
602+ this . subsTermOpen = false ;
603+ this . subsDeptOpen = false ;
604+ this . _loadCoursesForTerms ( ) ;
604605 this . singleRunIds = [ ] ;
605606 this . subsSelLeft = [ ] ;
606607 this . subsSelMiddle = [ ] ;
@@ -622,11 +623,21 @@ document.addEventListener("alpine:init", () => {
622623 this . rebuildSubsFiltered ( ) ;
623624 this . navigate ( "subscriptions" ) ;
624625 } ,
625- // ── Column data ─────────────────────────────────────────────────
626- get allCoursesForTerm ( ) {
627- if ( ! this . subsTerm ) return this . allCourses ;
628- return this . allCourses . filter ( function ( c ) { return c . term === this . subsTerm ; } , this ) ;
626+ // ── Load courses for selected terms (not all 20k) ──────────────
627+ _loadCoursesForTerms ( ) {
628+ var ids = new Set ( this . subsTerms ) ;
629+ var all = [ ] ;
630+ for ( var i = 0 ; i < this . allCoursesTerms . length ; i ++ ) {
631+ var t = this . allCoursesTerms [ i ] ;
632+ if ( ids . size === 0 || ids . has ( t ) ) {
633+ var rows = ICS . db . getAllCourses ( t ) ;
634+ for ( var j = 0 ; j < rows . length ; j ++ ) all . push ( rows [ j ] ) ;
635+ }
636+ }
637+ this . allCourses = all ;
638+ this . rebuildSubsFiltered ( ) ;
629639 } ,
640+ // ── Column data ─────────────────────────────────────────────────
630641 get subscribedCourses ( ) {
631642 var ids = new Set ( this . subscribedIds . map ( String ) ) ;
632643 return this . allCourses . filter ( function ( c ) { return ids . has ( String ( c . course_id ) ) ; } ) ;
@@ -635,17 +646,44 @@ document.addEventListener("alpine:init", () => {
635646 var ids = new Set ( this . singleRunIds . map ( String ) ) ;
636647 return this . allCourses . filter ( function ( c ) { return ids . has ( String ( c . course_id ) ) ; } ) ;
637648 } ,
649+ // ── Dropdown labels ─────────────────────────────────────────────
650+ get subsTermLabel ( ) {
651+ if ( ! this . subsTerms . length ) return '全部学期' ;
652+ return this . subsTerms . length + '个学期' ;
653+ } ,
654+ get subsDeptLabel ( ) {
655+ if ( ! this . subsDepts . length ) return '全部院系' ;
656+ return this . subsDepts . length + '个院系' ;
657+ } ,
658+ get subsDeptFiltered ( ) {
659+ var q = ( this . deptSearchQuery || '' ) . toLowerCase ( ) ;
660+ var deptSet = new Set ( ) ;
661+ for ( var i = 0 ; i < this . allCourses . length ; i ++ ) {
662+ var d = this . allCourses [ i ] . dept ;
663+ if ( d && ( ! q || d . toLowerCase ( ) . indexOf ( q ) !== - 1 ) ) deptSet . add ( d ) ;
664+ }
665+ return Array . from ( deptSet ) . sort ( ) ;
666+ } ,
667+ // ── Toggle multi-select ─────────────────────────────────────────
668+ toggleSubsTerm ( term , checked ) {
669+ var s = new Set ( this . subsTerms ) ;
670+ if ( checked ) s . add ( term ) ; else s . delete ( term ) ;
671+ this . subsTerms = Array . from ( s ) ;
672+ this . _loadCoursesForTerms ( ) ;
673+ } ,
674+ toggleSubsDept ( dept , checked ) {
675+ var s = new Set ( this . subsDepts ) ;
676+ if ( checked ) s . add ( dept ) ; else s . delete ( dept ) ;
677+ this . subsDepts = Array . from ( s ) ;
678+ this . rebuildSubsFiltered ( ) ;
679+ } ,
638680 // ── Filter middle column ─────────────────────────────────────────
639681 rebuildSubsFiltered ( ) {
640- var termQ = this . subsTerm ;
641- var deptQ = ( this . subsDept || "" ) . trim ( ) . toLowerCase ( ) ;
682+ var deptSet = new Set ( this . subsDepts . map ( function ( d ) { return d . toLowerCase ( ) ; } ) ) ;
642683 var titleQ = ( this . subsSearchTitle || "" ) . trim ( ) . toLowerCase ( ) ;
643684 var teacherQ = ( this . subsSearchTeacher || "" ) . trim ( ) . toLowerCase ( ) ;
644- var list = termQ
645- ? this . allCourses . filter ( function ( c ) { return c . term === termQ ; } )
646- : this . allCourses ;
647- this . subsFiltered = list . filter ( function ( c ) {
648- if ( deptQ && ( ! c . dept || c . dept . toLowerCase ( ) . indexOf ( deptQ ) === - 1 ) ) return false ;
685+ this . subsFiltered = this . allCourses . filter ( function ( c ) {
686+ if ( deptSet . size && ( ! c . dept || ! deptSet . has ( c . dept . toLowerCase ( ) ) ) ) return false ;
649687 if ( titleQ && ( ! c . title || c . title . toLowerCase ( ) . indexOf ( titleQ ) === - 1 ) ) return false ;
650688 if ( teacherQ && ( ! c . teacher || c . teacher . toLowerCase ( ) . indexOf ( teacherQ ) === - 1 ) ) return false ;
651689 return true ;
0 commit comments