@@ -66,8 +66,7 @@ const App = (() => {
6666 organizations : [ ] ,
6767 pullRequests : {
6868 incoming : [ ] ,
69- outgoing : [ ] ,
70- drafts : [ ]
69+ outgoing : [ ]
7170 } ,
7271 isDemoMode : false ,
7372 } ;
@@ -146,8 +145,7 @@ const App = (() => {
146145 // Categorize PRs
147146 state . pullRequests = {
148147 incoming : [ ] ,
149- outgoing : [ ] ,
150- drafts : [ ]
148+ outgoing : [ ]
151149 } ;
152150
153151 for ( const pr of prs ) {
@@ -156,9 +154,8 @@ const App = (() => {
156154 pr . status_tags = getStatusTags ( pr ) ;
157155 pr . last_activity = generateMockActivity ( pr ) ;
158156
159- if ( pr . draft ) {
160- state . pullRequests . drafts . push ( pr ) ;
161- } else if ( pr . user . login === state . currentUser . login ) {
157+ // Include drafts in incoming/outgoing based on author
158+ if ( pr . user . login === state . currentUser . login ) {
162159 state . pullRequests . outgoing . push ( pr ) ;
163160 } else {
164161 state . pullRequests . incoming . push ( pr ) ;
@@ -229,8 +226,7 @@ const App = (() => {
229226 // Extract unique organizations from PRs
230227 const allPRs = [
231228 ...state . pullRequests . incoming ,
232- ...state . pullRequests . outgoing ,
233- ...state . pullRequests . drafts
229+ ...state . pullRequests . outgoing
234230 ] ;
235231
236232 const uniqueOrgs = [ ...new Set ( allPRs . map ( pr => pr . repository . full_name . split ( '/' ) [ 0 ] ) ) ] . sort ( ) ;
@@ -255,45 +251,17 @@ const App = (() => {
255251 // Update counts
256252 $ ( 'incomingCount' ) . textContent = state . pullRequests . incoming . length ;
257253 $ ( 'outgoingCount' ) . textContent = state . pullRequests . outgoing . length ;
258- $ ( 'draftCount' ) . textContent = state . pullRequests . drafts . length ;
259-
260- // Update blocked counts
261- const incomingBlocked = state . pullRequests . incoming . filter ( pr =>
262- pr . status_tags ?. includes ( 'blocked on you' )
263- ) . length ;
264- const outgoingBlocked = state . pullRequests . outgoing . filter ( pr =>
265- pr . status_tags ?. includes ( 'blocked on you' )
266- ) . length ;
267-
268- const incomingBlockedEl = $ ( 'incomingBlockedCount' ) ;
269- const outgoingBlockedEl = $ ( 'outgoingBlockedCount' ) ;
270-
271- if ( incomingBlocked > 0 ) {
272- incomingBlockedEl . textContent = `${ incomingBlocked } blocked on you` ;
273- show ( incomingBlockedEl ) ;
274- } else {
275- hide ( incomingBlockedEl ) ;
276- }
277-
278- if ( outgoingBlocked > 0 ) {
279- outgoingBlockedEl . textContent = `${ outgoingBlocked } blocked on you` ;
280- show ( outgoingBlockedEl ) ;
281- } else {
282- hide ( outgoingBlockedEl ) ;
283- }
284254
285- // Update sparklines
286- updateSparklines ( ) ;
255+ // Update filter counts
256+ updateFilterCounts ( ) ;
287257
288258 // Render PR lists
289- renderPRList ( $ ( 'incomingPRs' ) , state . pullRequests . incoming ) ;
290- renderPRList ( $ ( 'outgoingPRs' ) , state . pullRequests . outgoing ) ;
291- renderPRList ( $ ( 'draftPRs' ) , state . pullRequests . drafts , true ) ;
259+ renderPRList ( $ ( 'incomingPRs' ) , state . pullRequests . incoming , false , 'incoming' ) ;
260+ renderPRList ( $ ( 'outgoingPRs' ) , state . pullRequests . outgoing , false , 'outgoing' ) ;
292261
293262 // Update empty state
294263 const totalPRs = state . pullRequests . incoming . length +
295- state . pullRequests . outgoing . length +
296- state . pullRequests . drafts . length ;
264+ state . pullRequests . outgoing . length ;
297265
298266 const emptyState = $ ( 'emptyState' ) ;
299267 if ( totalPRs === 0 ) {
@@ -303,57 +271,91 @@ const App = (() => {
303271 }
304272 } ;
305273
306- const updateSparklines = ( ) => {
307- // Simple sparkline implementation
308- const createSparkline = ( data , width = 60 , height = 20 , color = '#10b981' ) => {
309- if ( ! data . length ) return '' ;
274+ const updateFilterCounts = ( ) => {
275+ // Count stale and blocked on others PRs for each section
276+ const sections = [
277+ { prs : state . pullRequests . incoming , prefix : 'incoming' } ,
278+ { prs : state . pullRequests . outgoing , prefix : 'outgoing' }
279+ ] ;
280+
281+ sections . forEach ( ( { prs, prefix } ) => {
282+ const staleCount = prs . filter ( pr => pr . status_tags ?. includes ( 'stale' ) ) . length ;
283+ const blockedOthersCount = prs . filter ( pr =>
284+ ! pr . status_tags ?. includes ( 'blocked on you' ) && pr . status_tags ?. length > 0
285+ ) . length ;
310286
311- const max = Math . max ( ...data , 1 ) ;
312- const points = data . map ( ( value , index ) => {
313- const x = ( index / ( data . length - 1 ) ) * width ;
314- const y = height - ( value / max ) * height ;
315- return `${ x } ,${ y } ` ;
316- } ) . join ( ' ' ) ;
287+ // Update checkbox labels with counts
288+ const staleLabel = $ ( `${ prefix } FilterStale` ) ?. nextElementSibling ;
289+ const blockedOthersLabel = $ ( `${ prefix } FilterBlockedOthers` ) ?. nextElementSibling ;
317290
318- return `
319- <svg width="${ width } " height="${ height } " viewBox="0 0 ${ width } ${ height } ">
320- <polyline
321- fill="none"
322- stroke="${ color } "
323- stroke-width="2"
324- points="${ points } "
325- />
326- </svg>
327- ` ;
328- } ;
329-
330- // Mock data for sparklines
331- const incomingData = [ 3 , 5 , 2 , 8 , 4 , 7 , 6 ] ;
332- const outgoingData = [ 2 , 4 , 6 , 3 , 7 , 5 , 8 ] ;
333- const draftData = [ 1 , 2 , 1 , 3 , 2 , 4 , 3 ] ;
334-
335- $ ( 'incomingSparkline' ) . innerHTML = createSparkline ( incomingData , 60 , 20 , '#6366f1' ) ;
336- $ ( 'outgoingSparkline' ) . innerHTML = createSparkline ( outgoingData , 60 , 20 , '#10b981' ) ;
337- $ ( 'draftSparkline' ) . innerHTML = createSparkline ( draftData , 60 , 20 , '#94a3b8' ) ;
291+ if ( staleLabel ) {
292+ staleLabel . textContent = `Stale (${ staleCount } )` ;
293+ }
294+
295+ if ( blockedOthersLabel ) {
296+ blockedOthersLabel . textContent = `Blocked on Others (${ blockedOthersCount } )` ;
297+ }
298+ } ) ;
299+ } ;
300+
301+ const updateAverages = ( section , filteredPRs ) => {
302+ // Calculate average age for filtered PRs
303+ if ( filteredPRs . length === 0 ) {
304+ const avgElement = $ ( `${ section } Average` ) ;
305+ if ( avgElement ) avgElement . textContent = '' ;
306+ return ;
307+ }
338308
339- // Calculate averages
340- const avgIncoming = Math . round ( state . pullRequests . incoming . reduce ( ( sum , pr ) => sum + pr . age_days , 0 ) / state . pullRequests . incoming . length ) || 0 ;
341- const avgOutgoing = Math . round ( state . pullRequests . outgoing . reduce ( ( sum , pr ) => sum + pr . age_days , 0 ) / state . pullRequests . outgoing . length ) || 0 ;
309+ const avgAge = Math . round ( filteredPRs . reduce ( ( sum , pr ) => sum + pr . age_days , 0 ) / filteredPRs . length ) || 0 ;
310+ const avgElement = $ ( `${ section } Average` ) ;
342311
343- if ( avgIncoming > 0 ) $ ( 'incomingAverage' ) . textContent = `avg ${ avgIncoming } d` ;
344- if ( avgOutgoing > 0 ) $ ( 'outgoingAverage' ) . textContent = `avg ${ avgOutgoing } d` ;
312+ if ( avgAge > 0 && avgElement ) {
313+ avgElement . textContent = `avg ${ avgAge } d open` ;
314+ } else if ( avgElement ) {
315+ avgElement . textContent = '' ;
316+ }
345317 } ;
346318
347- const renderPRList = ( container , prs , isDraft = false ) => {
319+ const renderPRList = ( container , prs , isDraft = false , section = '' ) => {
348320 if ( ! container ) return ;
349321
350322 const orgSelect = $ ( 'orgSelect' ) ;
351323 const selectedOrg = orgSelect ?. value ;
352324
353- // Filter by organization
325+ // Get section-specific filter states from cookies or default to true
326+ let showStale = true ;
327+ let showBlockedOthers = true ;
328+
329+ if ( section === 'incoming' ) {
330+ showStale = getCookie ( 'incomingFilterStale' ) !== 'false' ;
331+ showBlockedOthers = getCookie ( 'incomingFilterBlockedOthers' ) !== 'false' ;
332+ // Update checkbox states from cookies
333+ if ( $ ( 'incomingFilterStale' ) ) $ ( 'incomingFilterStale' ) . checked = showStale ;
334+ if ( $ ( 'incomingFilterBlockedOthers' ) ) $ ( 'incomingFilterBlockedOthers' ) . checked = showBlockedOthers ;
335+ } else if ( section === 'outgoing' ) {
336+ showStale = getCookie ( 'outgoingFilterStale' ) !== 'false' ;
337+ showBlockedOthers = getCookie ( 'outgoingFilterBlockedOthers' ) !== 'false' ;
338+ // Update checkbox states from cookies
339+ if ( $ ( 'outgoingFilterStale' ) ) $ ( 'outgoingFilterStale' ) . checked = showStale ;
340+ if ( $ ( 'outgoingFilterBlockedOthers' ) ) $ ( 'outgoingFilterBlockedOthers' ) . checked = showBlockedOthers ;
341+ }
342+
343+ // Apply filters
354344 let filteredPRs = prs ;
345+
346+ // Filter by organization
355347 if ( selectedOrg ) {
356- filteredPRs = prs . filter ( pr => pr . repository . full_name . startsWith ( selectedOrg + '/' ) ) ;
348+ filteredPRs = filteredPRs . filter ( pr => pr . repository . full_name . startsWith ( selectedOrg + '/' ) ) ;
349+ }
350+
351+ // Filter stale PRs
352+ if ( ! showStale ) {
353+ filteredPRs = filteredPRs . filter ( pr => ! pr . status_tags ?. includes ( 'stale' ) ) ;
354+ }
355+
356+ // Filter blocked on others PRs
357+ if ( ! showBlockedOthers ) {
358+ filteredPRs = filteredPRs . filter ( pr => pr . status_tags ?. includes ( 'blocked on you' ) ) ;
357359 }
358360
359361 // Sort by priority
@@ -366,19 +368,24 @@ const App = (() => {
366368 } ) ;
367369
368370 container . innerHTML = sortedPRs . map ( pr => createPRCard ( pr , isDraft ) ) . join ( '' ) ;
371+
372+ // Update average for this section with filtered PRs
373+ if ( section === 'incoming' || section === 'outgoing' ) {
374+ updateAverages ( section , filteredPRs ) ;
375+ }
369376 } ;
370377
371378 const createPRCard = ( pr , isDraft = false ) => {
372- const state = getPRState ( pr , isDraft ) ;
373- const badges = buildBadges ( pr , isDraft ) ;
379+ const state = getPRState ( pr , pr . draft ) ;
380+ const badges = buildBadges ( pr , pr . draft ) ;
374381 const ageText = getAgeText ( pr ) ;
375382 const activityText = pr . last_activity ?
376383 ` <span class="activity-text">• ${ pr . last_activity . message } ${ formatTimeAgo ( pr . last_activity . timestamp ) } </span>` : '' ;
377384 const reviewers = buildReviewers ( pr . requested_reviewers || [ ] ) ;
378385 const needsAction = pr . status_tags ?. includes ( 'blocked on you' ) ;
379386
380387 return `
381- <div class="pr-card" data-state="${ state } " data-pr-id="${ pr . id } " ${ needsAction ? 'data-needs-action="true"' : '' } >
388+ <div class="pr-card" data-state="${ state } " data-pr-id="${ pr . id } " ${ needsAction ? 'data-needs-action="true"' : '' } ${ pr . draft ? 'data-draft="true"' : '' } >
382389 <div class="pr-header">
383390 <a href="${ pr . html_url } " class="pr-title" target="_blank" rel="noopener">
384391 ${ escapeHtml ( pr . title ) }
@@ -512,8 +519,7 @@ const App = (() => {
512519 // Find PR in all sections
513520 const allPRs = [
514521 ...state . pullRequests . incoming ,
515- ...state . pullRequests . outgoing ,
516- ...state . pullRequests . drafts
522+ ...state . pullRequests . outgoing
517523 ] ;
518524 const pr = allPRs . find ( p => p . id . toString ( ) === prId ) ;
519525 if ( ! pr ) return ;
@@ -548,7 +554,7 @@ const App = (() => {
548554 if ( response . ok ) {
549555 showToast ( 'PR merged successfully' , 'success' ) ;
550556 // Remove PR from state
551- [ 'incoming' , 'outgoing' , 'drafts' ] . forEach ( section => {
557+ [ 'incoming' , 'outgoing' ] . forEach ( section => {
552558 const index = state . pullRequests [ section ] . findIndex ( p => p . id . toString ( ) === prId ) ;
553559 if ( index !== - 1 ) {
554560 state . pullRequests [ section ] . splice ( index , 1 ) ;
@@ -612,7 +618,7 @@ const App = (() => {
612618 if ( response . ok ) {
613619 showToast ( 'PR closed' , 'success' ) ;
614620 // Remove PR from state
615- [ 'incoming' , 'outgoing' , 'drafts' ] . forEach ( section => {
621+ [ 'incoming' , 'outgoing' ] . forEach ( section => {
616622 const index = state . pullRequests [ section ] . findIndex ( p => p . id . toString ( ) === prId ) ;
617623 if ( index !== - 1 ) {
618624 state . pullRequests [ section ] . splice ( index , 1 ) ;
@@ -802,8 +808,7 @@ const App = (() => {
802808 // Enhance demo PRs
803809 const allPRs = [
804810 ...state . pullRequests . incoming ,
805- ...state . pullRequests . outgoing ,
806- ...state . pullRequests . drafts
811+ ...state . pullRequests . outgoing
807812 ] ;
808813
809814 allPRs . forEach ( pr => {
@@ -842,6 +847,26 @@ const App = (() => {
842847 }
843848 if ( loginBtn ) loginBtn . addEventListener ( 'click' , initiateLogin ) ;
844849
850+ // Setup filter event listeners for each section
851+ [ 'incoming' , 'outgoing' ] . forEach ( section => {
852+ const staleFilter = $ ( `${ section } FilterStale` ) ;
853+ const blockedOthersFilter = $ ( `${ section } FilterBlockedOthers` ) ;
854+
855+ if ( staleFilter ) {
856+ staleFilter . addEventListener ( 'change' , ( e ) => {
857+ setCookie ( `${ section } FilterStale` , e . target . checked . toString ( ) , 365 ) ;
858+ updatePRSections ( ) ;
859+ } ) ;
860+ }
861+
862+ if ( blockedOthersFilter ) {
863+ blockedOthersFilter . addEventListener ( 'change' , ( e ) => {
864+ setCookie ( `${ section } FilterBlockedOthers` , e . target . checked . toString ( ) , 365 ) ;
865+ updatePRSections ( ) ;
866+ } ) ;
867+ }
868+ } ) ;
869+
845870 // Add event listener for PAT input Enter key
846871 const patInput = $ ( 'patInput' ) ;
847872 if ( patInput ) {
@@ -892,7 +917,8 @@ const App = (() => {
892917 showMainContent ( ) ;
893918 } catch ( error ) {
894919 console . error ( 'Error initializing app:' , error ) ;
895- showToast ( 'Failed to load data' , 'error' ) ;
920+ const errorMessage = error . message || 'Unknown error' ;
921+ showToast ( `Failed to load data: ${ errorMessage } ` , 'error' ) ;
896922 }
897923 } ;
898924
0 commit comments