@@ -423,31 +423,78 @@ document.addEventListener('DOMContentLoaded', () => {
423423 generateBtn . disabled = true ;
424424 }
425425
426- function showPopupMessage ( message ) {
427- if ( ! message ) return ;
428-
429- // Materialize toast if available
430- if ( window . Materialize && typeof window . Materialize . toast === 'function' ) {
431- window . Materialize . toast ( message , 4000 ) ;
426+ function updateGenerateButtonState ( ) {
427+ const generateBtn = document . getElementById ( 'generateReport' ) ;
428+ const platformUsername = document . getElementById ( 'platformUsername' ) ;
429+ if ( ! generateBtn || ! platformUsername ) {
432430 return ;
433431 }
434432
433+ const applyGenerateButtonState = ( ) => {
434+ const hasUsername = Boolean ( platformUsername . value . trim ( ) ) ;
435+ const shouldDisable = ! hasUsername ;
436+
437+ if ( generateBtn . disabled !== shouldDisable ) {
438+ generateBtn . disabled = shouldDisable ;
439+ }
440+ } ;
441+
442+ if ( ! platformUsername . dataset . generateButtonStateBound ) {
443+ platformUsername . addEventListener ( 'input' , applyGenerateButtonState ) ;
444+ platformUsername . addEventListener ( 'change' , applyGenerateButtonState ) ;
445+ platformUsername . dataset . generateButtonStateBound = 'true' ;
446+ }
447+
448+ if ( ! generateBtn . dataset . generateButtonStateObserved && typeof MutationObserver !== 'undefined' ) {
449+ const observer = new MutationObserver ( ( ) => {
450+ const shouldDisable = ! platformUsername . value . trim ( ) ;
451+ if ( shouldDisable && generateBtn . disabled === false ) {
452+ generateBtn . disabled = true ;
453+ }
454+ } ) ;
455+
456+ observer . observe ( generateBtn , {
457+ attributes : true ,
458+ attributeFilter : [ 'disabled' ]
459+ } ) ;
460+
461+ generateBtn . dataset . generateButtonStateObserved = 'true' ;
462+ }
463+
464+ applyGenerateButtonState ( ) ;
465+ }
466+
467+ window . updateGenerateButtonState = updateGenerateButtonState ;
468+
469+ function showPopupMessage ( message ) {
470+ if ( ! message ) return ;
471+ const isDarkMode = document . body ?. classList . contains ( 'dark-mode' ) ;
472+
435473 const old = document . getElementById ( 'scrum-cache-toast' ) ;
436474 if ( old ) old . remove ( ) ;
437475
438476 const toast = document . createElement ( 'div' ) ;
439477 toast . id = 'scrum-cache-toast' ;
440- toast . className = 'toast' ;
441- toast . style . background = '#2563eb' ;
442- toast . style . color = '#fff' ;
478+ toast . className = 'scrum-cache-toast-custom' ;
479+ toast . style . background = isDarkMode ? '#ffffff' : '#1f2937' ;
480+ toast . style . color = isDarkMode ? '#1f2937' : '#fff' ;
481+ toast . style . border = 'none' ;
443482 toast . style . fontWeight = 'bold' ;
444- toast . style . padding = '12px 24px ' ;
483+ toast . style . padding = '12px 16px ' ;
445484 toast . style . borderRadius = '8px' ;
446485 toast . style . position = 'fixed' ;
447- toast . style . top = '24px ' ;
486+ toast . style . top = '12px ' ;
448487 toast . style . left = '50%' ;
449488 toast . style . transform = 'translateX(-50%)' ;
450- toast . style . zIndex = '9999' ;
489+ toast . style . zIndex = '2147483647' ;
490+ toast . style . width = 'calc(82% - 16px)' ;
491+ toast . style . maxWidth = '340px' ;
492+ toast . style . lineHeight = '1.4' ;
493+ toast . style . textAlign = 'start' ;
494+ toast . style . boxSizing = 'border-box' ;
495+ toast . style . wordBreak = 'break-word' ;
496+ toast . style . opacity = '1' ;
497+ toast . style . boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)' ;
451498 toast . textContent = message ;
452499
453500 document . body . appendChild ( toast ) ;
@@ -581,6 +628,7 @@ document.addEventListener('DOMContentLoaded', () => {
581628 const startingDateInput = document . getElementById ( 'startingDate' ) ;
582629 const endingDateInput = document . getElementById ( 'endingDate' ) ;
583630 const platformUsername = document . getElementById ( 'platformUsername' ) ;
631+ const usernameError = document . getElementById ( "usernameError" ) ;
584632
585633 browser . storage . local
586634 . get ( [
@@ -663,6 +711,7 @@ document.addEventListener('DOMContentLoaded', () => {
663711 const platform = result . platform || 'github' ;
664712 const platformUsernameKey = `${ platform } Username` ;
665713 platformUsername . value = result [ platformUsernameKey ] || '' ;
714+ window . updateGenerateButtonState && window . updateGenerateButtonState ( ) ;
666715 checkTokenForShowCommits ( ) ;
667716 checkTokenForMergedPRs ( ) ;
668717 } ,
@@ -684,40 +733,49 @@ document.addEventListener('DOMContentLoaded', () => {
684733 return ;
685734 }
686735
687- browser . tabs
688- . query ( { active : true , currentWindow : true } )
736+ // Helper to handle insert-to-email failures consistently
737+ const handleInsertFailure = ( errorMsg ) => {
738+ console . warn ( 'Insert to Email failed:' , errorMsg ) ;
739+ const failureMessage =
740+ browser . i18n . getMessage ( 'insertToEmailFailedError' ) ||
741+ 'open an email tab to insert report' ;
742+ showPopupMessage ( failureMessage ) ;
743+ } ;
744+
745+ browser . tabs . query ( { active : true , currentWindow : true } )
689746 . then ( ( tabs ) => {
690747 const tabId = tabs ?. [ 0 ] ?. id ;
691748 if ( ! tabId ) {
692- insertBtn . _triggeredByShortcut = false ;
749+ handleInsertFailure ( 'No active tab found' ) ;
693750 return ;
694751 }
695752
696- browser . tabs
697- . sendMessage ( tabId , { action : 'insertReportToEmail' , content, subject } )
753+ browser . tabs . sendMessage ( tabId , { action : 'insertReportToEmail' , content, subject } )
698754 . then ( ( response ) => {
699- if ( response ?. success && insertBtn . _triggeredByShortcut ) {
755+ if ( ! response ?. success ) {
756+ handleInsertFailure ( response ?. error ) ;
757+ } else if ( insertBtn . _triggeredByShortcut ) {
700758 showShortcutNotification ( 'insertedInEmailNotification' ) ;
701- } else if ( ! response ?. success ) {
702- console . warn ( 'Insert to Email failed:' , response ?. error ) ;
703759 }
704760 } )
705761 . catch ( ( error ) => {
706- console . warn ( 'Insert to Email failed:' , error ?. message || error ) ;
707- } )
708- . finally ( ( ) => {
709- insertBtn . _triggeredByShortcut = false ;
762+ handleInsertFailure ( error . message ) ;
710763 } ) ;
711764 } )
712765 . catch ( ( error ) => {
713- console . warn ( 'Unable to get active tab:' , error ?. message || error ) ;
766+ handleInsertFailure ( 'Failed to query tabs: ' + error . message ) ;
767+ } )
768+ . finally ( ( ) => {
714769 insertBtn . _triggeredByShortcut = false ;
715770 } ) ;
716771 } ) ;
717772 }
718773
719774 generateBtn . addEventListener ( 'click' , ( ) => {
720775 browser . storage . local . get ( [ 'platform' ] ) . then ( ( result ) => {
776+ platformUsername . classList . remove ( "input-error" ) ;
777+ usernameError . classList . remove ( "errorMessage" ) ;
778+ usernameError . textContent = "" ;
721779 const platform = result . platform || 'github' ;
722780 const platformUsernameKey = `${ platform } Username` ;
723781
@@ -1032,13 +1090,14 @@ document.addEventListener('DOMContentLoaded', () => {
10321090 startingDateInput . max = today ;
10331091 endingDateInput . max = today ;
10341092
1035-
1093+ // Save username to storage on input and update button state
10361094 platformUsername . addEventListener ( 'input' , ( ) => {
10371095 browser . storage . local . get ( [ 'platform' ] ) . then ( ( result ) => {
10381096 const platform = result . platform || 'github' ;
10391097 const platformUsernameKey = `${ platform } Username` ;
10401098 browser . storage . local . set ( { [ platformUsernameKey ] : platformUsername . value } ) ;
10411099 } ) ;
1100+ window . updateGenerateButtonState && window . updateGenerateButtonState ( ) ;
10421101 } ) ;
10431102 // Bootstrap report on popup open (restore cache / auto-generate / expired-cache toast)
10441103 bootstrapScrumReportOnPopupLoad ( generateBtn ) . catch ( ( err ) => {
@@ -1113,7 +1172,7 @@ document.addEventListener('DOMContentLoaded', () => {
11131172 try {
11141173 const items = await browser . storage . local . get ( [ 'platform' ] ) ;
11151174 platform = items . platform || 'github' ;
1116- } catch { }
1175+ } catch { }
11171176 if ( platform !== 'github' ) {
11181177 // Do not run repo fetch for non-GitHub platforms
11191178 if ( repoStatus ) repoStatus . textContent = chrome ?. i18n . getMessage ( 'repoFilteringGithubOnly' ) || 'Repository filtering is only available for GitHub.' ;
@@ -1201,7 +1260,7 @@ document.addEventListener('DOMContentLoaded', () => {
12011260 try {
12021261 const items = await browser . storage . local . get ( [ 'platform' ] ) ;
12031262 platform = items . platform || 'github' ;
1204- } catch { }
1263+ } catch { }
12051264 if ( platform !== 'github' ) {
12061265 repoFilterContainer . classList . add ( 'hidden' ) ;
12071266 useRepoFilter . checked = false ;
@@ -1382,7 +1441,7 @@ document.addEventListener('DOMContentLoaded', () => {
13821441 try {
13831442 const items = await browser . storage . local . get ( [ 'platform' ] ) ;
13841443 platform = items . platform || 'github' ;
1385- } catch { }
1444+ } catch { }
13861445 if ( platform !== 'github' ) {
13871446 if ( repoStatus ) repoStatus . textContent = chrome ?. i18n . getMessage ( 'repoLoadingGithubOnly' ) || 'Repository loading is only available for GitHub.' ;
13881447 return ;
@@ -1422,7 +1481,7 @@ document.addEventListener('DOMContentLoaded', () => {
14221481 try {
14231482 const items = await browser . storage . local . get ( [ 'platform' ] ) ;
14241483 platform = items . platform || 'github' ;
1425- } catch ( e ) { }
1484+ } catch ( e ) { }
14261485 if ( platform !== 'github' ) {
14271486 if ( repoStatus ) repoStatus . textContent = chrome ?. i18n . getMessage ( 'repoFetchingGithubOnly' ) || 'Repository fetching is only available for GitHub.' ;
14281487 return ;
@@ -1747,6 +1806,7 @@ platformSelect.addEventListener('change', () => {
17471806 browser . storage . local . get ( [ `${ platform } Username` ] ) . then ( ( result ) => {
17481807 if ( platformUsername ) {
17491808 platformUsername . value = result [ `${ platform } Username` ] || '' ;
1809+ window . updateGenerateButtonState && window . updateGenerateButtonState ( ) ;
17501810 }
17511811 } ) ;
17521812
@@ -1798,6 +1858,7 @@ function setPlatformDropdown(value) {
17981858 browser . storage . local . get ( [ `${ value } Username` ] ) . then ( ( result ) => {
17991859 if ( platformUsername ) {
18001860 platformUsername . value = result [ `${ value } Username` ] || '' ;
1861+ window . updateGenerateButtonState && window . updateGenerateButtonState ( ) ;
18011862 }
18021863 } ) ;
18031864
@@ -1814,6 +1875,11 @@ dropdownList.querySelectorAll('li').forEach((item) => {
18141875 item . addEventListener ( 'click' , function ( e ) {
18151876 const newPlatform = this . getAttribute ( 'data-value' ) ;
18161877 const currentPlatform = platformSelectHidden . value ;
1878+ const platformUsername = document . getElementById ( 'platformUsername' ) ;
1879+ const usernameError = document . getElementById ( "usernameError" ) ;
1880+ platformUsername . classList . remove ( "input-error" ) ;
1881+ usernameError . classList . remove ( "errorMessage" ) ;
1882+ usernameError . textContent = "" ;
18171883
18181884 if ( newPlatform !== currentPlatform ) {
18191885 const platformUsername = document . getElementById ( 'platformUsername' ) ;
@@ -1993,7 +2059,7 @@ document.getElementById('refreshCache').addEventListener('click', async function
19932059 try {
19942060 const items = await browser . storage . local . get ( [ 'platform' ] ) ;
19952061 platform = items . platform || 'github' ;
1996- } catch ( e ) { }
2062+ } catch ( e ) { }
19972063
19982064 // Clear all caches
19992065 const keysToRemove = [ 'githubCache' , 'repoCache' , 'gitlabCache' ] ;
0 commit comments