@@ -482,7 +482,9 @@ class App {
482482 const moduleName = module . name ;
483483
484484 const getLevelText = ( level : number ) => {
485- return level === 0 ? 'Not Unlocked' : `Level ${ level } / ${ module . maxLevel } ` ;
485+ if ( level === 0 ) return 'Not Unlocked' ;
486+ if ( level === module . maxLevel ) return `Level ${ level } (Max)` ;
487+ return `Level ${ level } / ${ module . maxLevel } ` ;
486488 } ;
487489
488490 const card = document . createElement ( 'div' ) ;
@@ -491,34 +493,136 @@ class App {
491493 <h3>${ moduleName } </h3>
492494 <div class="workshop-card__level">
493495 <span class="workshop-card__level-text">${ getLevelText ( currentLevel ) } </span>
494- <input
495- type="range"
496- min="0"
497- max="${ module . maxLevel } "
498- value="${ currentLevel } "
499- data-module-id="${ module . id } "
500- class="workshop-card__slider"
501- />
496+ <div class="workshop-card__ticks"></div>
502497 </div>
503498 ` ;
504499
505- const slider = card . querySelector ( '.workshop-card__slider' ) as HTMLInputElement ;
506500 const levelText = card . querySelector ( '.workshop-card__level-text' ) as HTMLSpanElement ;
501+ const ticksContainer = card . querySelector ( '.workshop-card__ticks' ) as HTMLDivElement ;
507502
508- slider . addEventListener ( 'input' , ( e ) => {
509- const newLevel = parseInt ( ( e . target as HTMLInputElement ) . value ) ;
503+ // Helper to update UI and save progress
504+ const updateProgress = ( newLevel : number ) => {
510505 levelText . textContent = getLevelText ( newLevel ) ;
511- } ) ;
512506
513- slider . addEventListener ( 'change' , ( e ) => {
514- const newLevel = parseInt ( ( e . target as HTMLInputElement ) . value ) ;
507+ // Update tick visual states
508+ ticksContainer . querySelectorAll ( '.workshop-card__tick' ) . forEach ( ( tick , idx ) => {
509+ tick . classList . toggle ( 'completed' , idx < newLevel ) ;
510+ } ) ;
511+
512+ // Save progress
515513 this . updateWorkshopLevel ( module . id , newLevel ) ;
516- } ) ;
514+ } ;
515+
516+ // Create tick marks for each level (1 to maxLevel)
517+ for ( let level = 1 ; level <= module . maxLevel ; level ++ ) {
518+ const tick = document . createElement ( 'div' ) ;
519+ tick . className = 'workshop-card__tick' ;
520+ if ( level <= currentLevel ) {
521+ tick . classList . add ( 'completed' ) ;
522+ }
523+ tick . dataset . moduleId = module . id ;
524+ tick . dataset . level = String ( level ) ;
525+
526+ // Find level data for requirements
527+ const levelData = module . levels . find ( l => l . level === level ) ;
528+
529+ // Add hover for popover
530+ tick . addEventListener ( 'mouseenter' , ( e ) => this . showHideoutPopover ( module , level , levelData , e ) ) ;
531+ tick . addEventListener ( 'mouseleave' , ( ) => this . hideHideoutPopover ( ) ) ;
532+
533+ // Click to set progress
534+ tick . addEventListener ( 'click' , ( ) => {
535+ const currentlyCompleted = tick . classList . contains ( 'completed' ) ;
536+ // If clicking on a completed level, set to previous level
537+ // If clicking on incomplete level, complete up to and including this one
538+ const newLevel = currentlyCompleted ? level - 1 : level ;
539+ updateProgress ( newLevel ) ;
540+ } ) ;
541+
542+ ticksContainer . appendChild ( tick ) ;
543+ }
517544
518545 workshopGrid . appendChild ( card ) ;
519546 } ) ;
520547 }
521548
549+ private showHideoutPopover ( module : any , level : number , levelData : any , event : MouseEvent ) {
550+ // Remove existing popover if any
551+ this . hideHideoutPopover ( ) ;
552+
553+ const popover = document . createElement ( 'div' ) ;
554+ popover . className = 'quest-popover' ; // Reuse quest popover styling
555+ popover . id = 'hideout-popover' ;
556+
557+ // Build requirements HTML
558+ let requirementsHtml = '<span class="quest-popover__none">None</span>' ;
559+ if ( levelData ?. requirementItemIds && levelData . requirementItemIds . length > 0 ) {
560+ requirementsHtml = levelData . requirementItemIds . map ( ( req : any ) => {
561+ const itemId = req . item_id || req . itemId ;
562+ const quantity = req . quantity || '?' ;
563+ const item = this . gameData . items . find ( i => i . id === itemId ) ;
564+ const itemName = item ?. name || itemId || 'Unknown' ;
565+ const iconUrl = item ? dataLoader . getIconUrl ( item ) : '' ;
566+
567+ return `
568+ <div class="quest-popover__item">
569+ ${ iconUrl ? `<img src="${ iconUrl } " alt="" class="quest-popover__item-icon" />` : '' }
570+ <span>${ quantity } x ${ itemName } </span>
571+ </div>
572+ ` ;
573+ } ) . join ( '' ) ;
574+ }
575+
576+ // Check for other requirements (like coins for stash)
577+ let otherReqsHtml = '' ;
578+ if ( levelData ?. otherRequirements && levelData . otherRequirements . length > 0 ) {
579+ otherReqsHtml = levelData . otherRequirements . map ( ( req : string ) => `
580+ <div class="quest-popover__item">
581+ <span>${ req } </span>
582+ </div>
583+ ` ) . join ( '' ) ;
584+ }
585+
586+ const allRequirementsHtml = requirementsHtml + otherReqsHtml || '<span class="quest-popover__none">None</span>' ;
587+
588+ popover . innerHTML = `
589+ <div class="quest-popover__title">${ module . name } - Level ${ level } </div>
590+ ${ levelData ?. description ? `<div class="quest-popover__desc">${ levelData . description } </div>` : '' }
591+ <div class="quest-popover__section">
592+ <div class="quest-popover__section-title">Requirements</div>
593+ <div class="quest-popover__items">${ allRequirementsHtml } </div>
594+ </div>
595+ ` ;
596+
597+ document . body . appendChild ( popover ) ;
598+
599+ // Position the popover near the tick (above it)
600+ const rect = ( event . target as HTMLElement ) . getBoundingClientRect ( ) ;
601+ const popoverRect = popover . getBoundingClientRect ( ) ;
602+
603+ let left = rect . left + rect . width / 2 - popoverRect . width / 2 ;
604+ let top = rect . top - popoverRect . height - 8 ;
605+
606+ // Keep popover within viewport
607+ if ( left < 8 ) left = 8 ;
608+ if ( left + popoverRect . width > window . innerWidth - 8 ) {
609+ left = window . innerWidth - popoverRect . width - 8 ;
610+ }
611+ if ( top < 8 ) {
612+ top = rect . bottom + 8 ;
613+ }
614+
615+ popover . style . left = `${ left } px` ;
616+ popover . style . top = `${ top } px` ;
617+ }
618+
619+ private hideHideoutPopover ( ) {
620+ const existing = document . getElementById ( 'hideout-popover' ) ;
621+ if ( existing ) {
622+ existing . remove ( ) ;
623+ }
624+ }
625+
522626 private updateWorkshopLevel ( moduleId : string , level : number ) {
523627 this . userProgress . hideoutLevels [ moduleId ] = level ;
524628 StorageManager . saveUserProgress ( this . userProgress ) ;
0 commit comments