11<script lang =" ts" >
2- import {
3- type subjects ,
4- type modules as modulesType ,
5- type lessons ,
6- type studentProgress
2+ import type {
3+ subjects ,
4+ modules as modulesType ,
5+ lessons ,
6+ studentProgress
77 } from ' $lib/server/db/schema' ;
8+ import { onMount } from ' svelte' ;
89
910 // Props
1011 let {
2526
2627 // Track expanded state for each module
2728 let expandedModules = $state <Record <number , boolean >>({});
29+ let previousModuleId = $state <number | null >(null );
2830
29- // Initialize expanded state for current module
31+ // Update expanded modules when currentModuleId changes
3032 $effect (() => {
31- if (currentModuleId && ! expandedModules [currentModuleId ]) {
33+ if (currentModuleId && currentModuleId !== previousModuleId ) {
34+ // Close previous module if different from current
35+ if (previousModuleId && previousModuleId !== currentModuleId ) {
36+ expandedModules [previousModuleId ] = false ;
37+ }
38+
39+ // Open current module
3240 expandedModules [currentModuleId ] = true ;
41+
42+ // Update previous module reference
43+ previousModuleId = currentModuleId ;
3344 }
3445 });
3546
4152 // Get lessons for a specific module
4253 function getLessonsForModule(moduleId : number ) {
4354 return allLessons
44- .filter ((lesson : { moduleId: number ; }) => lesson .moduleId === moduleId )
45- .sort ((a : { orderInModule: any ; }, b : { orderInModule: any ; }) => (a ?.orderInModule || 0 ) - (b ?.orderInModule || 0 ));
55+ .filter ((lesson : { moduleId: number }) => lesson .moduleId === moduleId )
56+ .sort (
57+ (a : { orderInModule: any }, b : { orderInModule: any }) =>
58+ (a ?.orderInModule || 0 ) - (b ?.orderInModule || 0 )
59+ );
4660 }
4761
4862 // Check if a lesson is completed
4963 function isLessonCompleted(lessonId : number ) {
50- // Explicitly check if progress entry exists AND its completedAt is not null
5164 return progress [lessonId ] != null && progress [lessonId ].completedAt !== null ;
5265 }
5366 </script >
5467
55- <div class =" w-64 h-full rounded-lg bg-surface-100 dark:bg-surface-800 flex flex-col" >
56- <div class =" p-2 flex flex-col h-full" >
57- <h2 class =" mb-4 text-center text-lg font-semibold flex-shrink-0" >Lessons</h2 >
68+ <div class =" flex h-full w-64 flex-col rounded-lg bg-surface-50 shadow-md dark:bg-surface-800" >
69+ <div class =" flex h-full flex-col p-3" >
70+ <header class =" mb-3 border-b border-surface-200 pb-3 dark:border-surface-700" >
71+ <h2 class ="text-center text-lg font-semibold" >{subject ?.name || ' Lessons' }</h2 >
72+ </header >
5873
5974 <!-- Module list -->
60- <div class =" overflow-y-auto flex -1" >
61- <ul class =" space-y-2 " >
75+ <div class =" flex-1 overflow-y-auto pr -1" >
76+ <ul class =" space-y-3 " >
6277 {#each modules as module (module .id )}
63- <li class =" rounded-lg bg-surface-200 dark:bg-surface-700" >
78+ {@const isActive = module .id === currentModuleId }
79+
80+ <li
81+ class =" overflow-hidden rounded-lg bg-surface-100 shadow-sm dark:bg-surface-700
82+ {isActive ? 'ring-1 ring-tertiary-500' : ''}"
83+ >
6484 <!-- Module header -->
6585 <button
66- class =" flex w-full cursor-pointer items-center justify-between rounded-md p-2 text-left
67- {module.id === currentModuleId
68- ? 'bg-secondary-400 dark:bg-secondary-500 '
69- : 'bg-tertiary-500 dark:bg-tertiary-700 '}"
86+ class =" flex w-full cursor-pointer items-center justify-between p-3 text-left
87+ {isActive
88+ ? 'bg-tertiary-500 text-white dark:bg-tertiary-700 '
89+ : 'hover: bg-tertiary-200 dark:hover: bg-tertiary-800 '}"
7090 onclick ={() => toggleModule (module .id )}
7191 aria-expanded ={expandedModules [module .id ]}
7292 >
73- <span class ="font-semibold " >{module .name || ' Module' }</span >
74- <div class =" flex items-center " >
93+ <span class ="truncate font-medium " >{module .name || ' Module' }</span >
94+ <div class =" ml-1 flex-shrink-0 " >
7595 <svg
7696 xmlns =" http://www.w3.org/2000/svg"
7797 width =" 16"
82102 stroke-width =" 2"
83103 stroke-linecap =" round"
84104 stroke-linejoin =" round"
105+ class =" transition-transform duration-300"
106+ style ="transform: {expandedModules [module .id ]
107+ ? ' rotate(180deg)'
108+ : ' rotate(0deg)' }"
85109 >
86- <path d ={ expandedModules [ module . id ] ? ' M18 15l-6-6-6 6 ' : ' M6 9l6 6 6-6 ' } / >
110+ <polyline points = " 6 9 12 15 18 9 " ></ polyline >
87111 </svg >
88112 </div >
89113 </button >
98122 {@const isCompleted = isLessonCompleted (lesson .id )}
99123 {@const isCurrent = lesson .id === currentLessonId }
100124
101- <li
102- class ="cursor-pointer rounded p-2 {isCurrent
103- ? ' bg-tertiary-300 dark:bg-tertiary-900'
104- : isCompleted
105- ? ' bg-green-100 hover:bg-green-200 dark:bg-green-900 dark:hover:bg-green-800'
106- : ' hover:bg-tertiary-100 dark:hover:bg-tertiary-800' }"
107- >
125+ <li >
108126 <a
109127 href ={` /lessons/subject/${subject ?.id || ' ' }/module/${module .id || ' ' }/lesson/${lesson .id || ' ' } ` }
110- class =" flex w-full items-center justify-between text-left"
128+ class =" flex items-center rounded-md p-2 text-left transition-colors
129+ {isCurrent
130+ ? 'bg-tertiary-200 font-medium dark:bg-tertiary-900'
131+ : isCompleted
132+ ? 'hover:bg-surface-200 dark:hover:bg-surface-600'
133+ : 'hover:bg-surface-200 dark:hover:bg-surface-600'}"
111134 >
112- <span class ="truncate pr-2" >{lesson .title || ' Lesson' }</span >
113- {#if isCompleted }
114- <svg
115- xmlns =" http://www.w3.org/2000/svg"
116- width =" 14"
117- height =" 14"
118- viewBox =" 0 0 24 24"
119- fill =" none"
120- stroke =" currentColor"
121- stroke-width =" 2"
122- stroke-linecap =" round"
123- stroke-linejoin =" round"
124- class =" flex-shrink-0"
125- >
126- <path d =" M20 6L9 17l-5-5" />
127- </svg >
128- {/if }
135+ <div class =" mr-2 flex-shrink-0" >
136+ {#if isCompleted }
137+ <div
138+ class =" flex h-5 w-5 items-center justify-center rounded-full bg-success-600 dark:bg-success-700"
139+ >
140+ <svg
141+ xmlns =" http://www.w3.org/2000/svg"
142+ width =" 12"
143+ height =" 12"
144+ viewBox =" 0 0 24 24"
145+ fill =" none"
146+ stroke =" white"
147+ stroke-width =" 3"
148+ stroke-linecap =" round"
149+ stroke-linejoin =" round"
150+ >
151+ <polyline points =" 20 6 9 17 4 12" ></polyline >
152+ </svg >
153+ </div >
154+ {:else }
155+ <div
156+ class =" h-5 w-5 rounded-full border-2 border-surface-300 dark:border-surface-500
157+ {isCurrent ? 'border-tertiary-500 dark:border-tertiary-400' : ''}"
158+ ></div >
159+ {/if }
160+ </div >
161+ <span class ="truncate text-sm" >{lesson .title || ' Lesson' }</span >
129162 </a >
130163 </li >
131164 {/each }
132165 </ul >
133166 {:else }
134- <div class =" p-2 text-center text-sm italic text-gray-500" >No lessons available</div >
167+ <div class =" p-3 text-center text-sm italic text-gray-500" >No lessons available</div >
135168 {/if }
136169 {/if }
137170 </li >
138171 {/each }
139172 </ul >
140173
141174 {#if modules .length === 0 }
142- <div class =" mt-4 text-center text-gray-500" >No modules available</div >
175+ <div class =" p-4 text-center text-gray-500" >
176+ <svg
177+ xmlns =" http://www.w3.org/2000/svg"
178+ width =" 24"
179+ height =" 24"
180+ viewBox =" 0 0 24 24"
181+ fill =" none"
182+ stroke =" currentColor"
183+ stroke-width =" 2"
184+ stroke-linecap =" round"
185+ stroke-linejoin =" round"
186+ class =" mx-auto mb-2 text-gray-400"
187+ >
188+ <path d =" M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" ></path >
189+ <polyline points =" 13 2 13 9 20 9" ></polyline >
190+ </svg >
191+ <p >No modules available</p >
192+ </div >
143193 {/if }
144194 </div >
145195 </div >
146- </div >
196+ </div >
0 commit comments