@@ -29,7 +29,6 @@ const fieldClass =
2929 "mt-1 w-full rounded-token border border-border bg-background/40 px-3 py-2 text-token-sm text-foreground focus-ring" ;
3030
3131const DEFAULT_LIMIT = 50 ;
32- const MAX_LIMIT = 100 ;
3332
3433type AuditFeedProps = {
3534 enabled ?: boolean ;
@@ -41,20 +40,21 @@ export function AuditFeed({ enabled = true }: AuditFeedProps) {
4140 const [ repoFullName , setRepoFullName ] = useState ( "" ) ;
4241 const [ sinceInput , setSinceInput ] = useState ( "" ) ;
4342 const [ sinceIso , setSinceIso ] = useState ( "" ) ;
44- const [ limit , setLimit ] = useState ( DEFAULT_LIMIT ) ;
4543 const [ status , setStatus ] = useState < "loading" | "ready" | "error" > ( "loading" ) ;
4644 const [ error , setError ] = useState < string | null > ( null ) ;
4745 const [ data , setData ] = useState < SkippedPrAuditExport | null > ( null ) ;
46+ const [ loadingMore , setLoadingMore ] = useState ( false ) ;
4847
49- const queryPath = useMemo (
48+ const filterPath = useMemo (
5049 ( ) =>
5150 buildSkippedPrAuditPath ( {
52- limit,
51+ limit : DEFAULT_LIMIT ,
52+ offset : 0 ,
5353 repoFullName : repoFullName || undefined ,
5454 reason : reason || undefined ,
5555 since : sinceIso || undefined ,
5656 } ) ,
57- [ limit , reason , repoFullName , sinceIso ] ,
57+ [ reason , repoFullName , sinceIso ] ,
5858 ) ;
5959
6060 const load = useCallback ( async ( ) => {
@@ -67,7 +67,7 @@ export function AuditFeed({ enabled = true }: AuditFeedProps) {
6767 setStatus ( "loading" ) ;
6868 setError ( null ) ;
6969 const origin = getApiOrigin ( ) . replace ( / \/ $ / , "" ) ;
70- const result = await apiFetch < SkippedPrAuditExport > ( `${ origin } ${ queryPath } ` , {
70+ const result = await apiFetch < SkippedPrAuditExport > ( `${ origin } ${ filterPath } ` , {
7171 label : "Skipped PR audit" ,
7272 credentials : "include" ,
7373 headers : { Accept : "application/json" } ,
@@ -87,7 +87,7 @@ export function AuditFeed({ enabled = true }: AuditFeedProps) {
8787 setData ( null ) ;
8888 setError ( result . message ) ;
8989 setStatus ( "error" ) ;
90- } , [ enabled , queryPath ] ) ;
90+ } , [ enabled , filterPath ] ) ;
9191
9292 useEffect ( ( ) => {
9393 void load ( ) ;
@@ -96,7 +96,6 @@ export function AuditFeed({ enabled = true }: AuditFeedProps) {
9696 const applyFilters = ( ) => {
9797 setSinceIso ( normalizeSinceInput ( sinceInput ) ) ;
9898 setRepoFullName ( repoDraft . trim ( ) ) ;
99- setLimit ( DEFAULT_LIMIT ) ;
10099 } ;
101100
102101 const resetFilters = ( ) => {
@@ -105,11 +104,43 @@ export function AuditFeed({ enabled = true }: AuditFeedProps) {
105104 setRepoFullName ( "" ) ;
106105 setSinceInput ( "" ) ;
107106 setSinceIso ( "" ) ;
108- setLimit ( DEFAULT_LIMIT ) ;
109107 } ;
110108
111- const loadMore = ( ) => {
112- setLimit ( ( current ) => Math . min ( current + DEFAULT_LIMIT , MAX_LIMIT ) ) ;
109+ const loadMore = async ( ) => {
110+ if ( ! enabled || ! data ?. hasMore || loadingMore ) return ;
111+ setLoadingMore ( true ) ;
112+ setError ( null ) ;
113+ const nextOffset = data . items . length ;
114+ const origin = getApiOrigin ( ) . replace ( / \/ $ / , "" ) ;
115+ const path = buildSkippedPrAuditPath ( {
116+ limit : DEFAULT_LIMIT ,
117+ offset : nextOffset ,
118+ repoFullName : repoFullName || undefined ,
119+ reason : reason || undefined ,
120+ since : sinceIso || undefined ,
121+ } ) ;
122+ const result = await apiFetch < SkippedPrAuditExport > ( `${ origin } ${ path } ` , {
123+ label : "Skipped PR audit" ,
124+ credentials : "include" ,
125+ headers : { Accept : "application/json" } ,
126+ } ) ;
127+ setLoadingMore ( false ) ;
128+ if ( ! result . ok ) {
129+ setError ( result . message ) ;
130+ return ;
131+ }
132+ const normalized = normalizeSkippedPrAuditExport ( result . data ) ;
133+ if ( ! normalized ) {
134+ setError ( "The skipped PR audit endpoint returned an unexpected response." ) ;
135+ return ;
136+ }
137+ // Append-not-replace (#7438): keep already-rendered rows; only grow with the next page.
138+ setData ( {
139+ ...normalized ,
140+ items : [ ...data . items , ...normalized . items ] ,
141+ // Surface the next page's paging cursor so subsequent Load more advances correctly.
142+ offset : nextOffset ,
143+ } ) ;
113144 } ;
114145
115146 if ( status === "loading" && ! data ) {
@@ -138,10 +169,7 @@ export function AuditFeed({ enabled = true }: AuditFeedProps) {
138169 reason = { reason }
139170 repoDraft = { repoDraft }
140171 sinceInput = { sinceInput }
141- onReasonChange = { ( value ) => {
142- setReason ( value ) ;
143- setLimit ( DEFAULT_LIMIT ) ;
144- } }
172+ onReasonChange = { setReason }
145173 onRepoDraftChange = { setRepoDraft }
146174 onSinceInputChange = { setSinceInput }
147175 onApply = { applyFilters }
@@ -175,10 +203,7 @@ export function AuditFeed({ enabled = true }: AuditFeedProps) {
175203 reason = { reason }
176204 repoDraft = { repoDraft }
177205 sinceInput = { sinceInput }
178- onReasonChange = { ( value ) => {
179- setReason ( value ) ;
180- setLimit ( DEFAULT_LIMIT ) ;
181- } }
206+ onReasonChange = { setReason }
182207 onRepoDraftChange = { setRepoDraft }
183208 onSinceInputChange = { setSinceInput }
184209 onApply = { applyFilters }
@@ -249,14 +274,12 @@ export function AuditFeed({ enabled = true }: AuditFeedProps) {
249274 </ TableScroll >
250275
251276 < div className = "flex flex-wrap items-center gap-3" >
252- { data . hasMore && limit < MAX_LIMIT ? (
253- < StateActionButton onClick = { loadMore } > Load more</ StateActionButton >
254- ) : null }
255- { data . hasMore && limit >= MAX_LIMIT ? (
256- < p className = "text-token-xs text-muted-foreground" >
257- Showing the maximum page size ({ MAX_LIMIT } ). Narrow filters to inspect older events.
258- </ p >
277+ { data . hasMore ? (
278+ < StateActionButton onClick = { ( ) => void loadMore ( ) } disabled = { loadingMore } >
279+ { loadingMore ? "Loading…" : "Load more" }
280+ </ StateActionButton >
259281 ) : null }
282+ { error ? < p className = "text-token-xs text-destructive" > { error } </ p > : null }
260283 < StateActionButton onClick = { ( ) => void load ( ) } > Refresh</ StateActionButton >
261284 </ div >
262285 </ div >
0 commit comments