Trigger page UI optimizations#2253
Conversation
🦋 Changeset detectedLatest commit: 141931b The changes in this PR will be included in the next version bump. This PR includes changesets to release 10 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
There was a problem hiding this comment.
PR Review Summary
(4) Total Issues | Risk: Medium
🟠⚠️ Major (3) 🟠⚠️
🟠 1) triggers-tabs.tsx:17 Missing Suspense boundary for useSearchParams
Issue: The TriggersTabs component uses useSearchParams() (line 17) but is not wrapped in a Suspense boundary. Per Next.js App Router requirements, useSearchParams always requires a Suspense boundary in pages that can be statically rendered, otherwise the entire page becomes client-side rendered.
Why: Without a Suspense boundary around the component using useSearchParams, Next.js will bail out to client-side rendering for the entire page. This defeats the benefit of the RSC data fetching optimization introduced in this PR, as the page cannot be statically rendered or streamed.
Fix: Wrap TriggersTabs in a Suspense boundary in page.tsx:
import { Suspense } from 'react';
// In TriggersPage:
<Suspense fallback={null}>
<TriggersTabs
scheduledContent={...}
webhooksContent={...}
/>
</Suspense>Alternatively, pass the initial tab value from the server component to avoid needing useSearchParams in the client component.
Refs:
🟠 2) page.tsx:36-50 Error handling regression — errors now show generic message
Issue: The previous implementation had a try-catch with FullPageError that provided context-specific error messages (context="triggers"). The new RSC tab content components have no error handling — errors bubble up to the [tenantId]/error.tsx boundary which shows a generic "Something went wrong" message.
Why: Users will see less actionable error messages when data fetching fails. Instead of "Failed to load triggers", they see "Something went wrong. An unexpected error occurred." This degrades the debugging experience for both users and support teams.
Fix: Either:
- Add error boundaries around each Suspense-wrapped tab content:
import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary fallback={<FullPageError errorCode="fetch-error" context="scheduled triggers" />}>
<Suspense fallback={<TabSkeleton />}>
<ScheduledTabContent tenantId={tenantId} projectId={projectId} />
</Suspense>
</ErrorBoundary>- Or keep page-level try-catch with the new pattern by catching in the RSC content components and re-throwing with context.
Refs:
- evaluations/page.tsx:23-43 — established error handling pattern
- mcp-servers/page.tsx:63-65 — similar pattern
🟠 3) project-triggers.ts:51,78 Redundant fetchAgents calls when both tabs load
Issue: Both fetchProjectTriggers (line 51) and fetchProjectScheduledTriggers (line 78) independently call fetchAgents(tenantId, projectId). Since the RSC content components execute in parallel via separate Suspense boundaries, this results in two identical API calls for the same data.
Why: This doubles the agents API load and adds unnecessary latency. Previously, fetchAgents was called once in page.tsx and passed to TriggersTabs as a prop.
Fix: Add React's cache() wrapper to fetchAgents in agent-full-client.ts to deduplicate across the request:
// agent-full-client.ts
import { cache } from 'react';
export const fetchAgents = cache(async (
tenantId: string,
projectId: string
): Promise<ListResponse<Agent>> => {
// ... existing implementation
});This ensures the same request is deduped within a single render tree.
Refs:
Inline Comments:
- 🟠 Major:
triggers-tabs.tsx:31URL state pattern diverges from established convention - 🟡 Minor:
scheduled-tab-panel.tsx:30-32Unnecessary useEffect to sync initialTriggers
💭 Consider (2) 💭
💭 1) scheduled-tab-panel.tsx:27 / webhooks-tab-panel.tsx:21 Agent filter state is now independent per tab
Issue: Each tab panel now has its own useState for agentFilter. Previously, a single filter state was shared across both tabs. This means if a user filters by "Agent A" on Scheduled and switches to Webhooks, the filter resets.
Why: This may be intentional (tab-specific filters) or a regression (users expected persistence). Consider whether this matches product expectations.
Fix: If persistence is desired, lift agentFilter state to a parent component or persist in URL search params.
💭 2) webhooks-tab-panel.tsx Webhook tab lacks polling that scheduled tab has
Issue: The Scheduled tab polls every 3 seconds to refresh trigger data, but the Webhooks tab does not. Users on Webhooks won't see updates without manually refreshing.
Why: This asymmetry may be intentional — scheduled triggers have runtime state (next run time) while webhook triggers are static configuration. If it's intentional, the difference is acceptable.
🧹 While You're Here (1) 🧹
🧹 1) project-triggers.ts:62-64, 89-91, 127-129 Silent catch blocks return empty arrays without logging
Issue: (Pre-existing) When individual agent or trigger fetches fail, errors are completely swallowed with catch { return []; }. No logging, no user feedback.
Why: Users see a partial list with no indication that some agents failed to load. API errors, auth issues, or network problems are invisible. This makes debugging difficult.
Fix: Add logging with context:
catch (error) {
console.error('Failed to fetch triggers for agent', agent.id, error);
return [];
}🚫 REQUEST CHANGES
Summary: This PR introduces a good architectural pattern for tab-level data fetching with Suspense, but has a few issues that should be addressed before merging: (1) missing Suspense boundary for useSearchParams, (2) error handling regression that degrades user experience, and (3) redundant API calls. The URL state pattern change is valid but introduces inconsistency with other tab components — consider whether to adopt this as the new standard.
Discarded (3)
| Location | Issue | Reason Discarded |
|---|---|---|
project-triggers/index.ts |
Barrel export not updated for new components | Page imports directly by path consistently; no barrel export convention violated |
page.tsx:17-31 |
TabSkeleton defined inline rather than extracted | Acceptable pattern per codebase conventions; composed skeletons are typically defined where used |
| General | Standards review found no issues | No issues to report |
Reviewers (6)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
pr-review-frontend |
4 | 2 | 0 | 0 | 1 | 0 | 1 |
pr-review-product |
5 | 1 | 2 | 0 | 0 | 0 | 2 |
pr-review-consistency |
4 | 1 | 0 | 0 | 1 | 0 | 2 |
pr-review-architecture |
3 | 1 | 0 | 0 | 0 | 0 | 2 |
pr-review-errors |
5 | 0 | 0 | 1 | 0 | 0 | 4 |
pr-review-standards |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Total | 21 | 5 | 2 | 1 | 2 | 0 | 11 |
Note: Many findings from different reviewers addressed the same underlying issues (error handling, URL pattern, redundant fetches) and were merged during deduplication.
| useEffect(() => { | ||
| setTriggers(initialTriggers); | ||
| }, [initialTriggers]); |
There was a problem hiding this comment.
🟡 Minor: Unnecessary useEffect to sync initialTriggers to state
Issue: This useEffect syncs initialTriggers prop to local state, causing an extra render cycle when the prop changes.
Why: Per React best practices, deriving state from props should happen during render, not in effects. The component renders once with stale state, then the effect triggers a second render with updated state.
Fix: Remove this effect — the initial useState(initialTriggers) already handles initialization. Since this is an RSC pattern where the component remounts on navigation rather than receiving new props, this effect is not needed.
| useEffect(() => { | |
| setTriggers(initialTriggers); | |
| }, [initialTriggers]); |
Refs:
| params.set('tab', tab); | ||
| router.replace(`${pathname}?${params.toString()}`, { scroll: false }); | ||
| params.set('tab', newTab); | ||
| window.history.replaceState(null, '', `${pathname}?${params.toString()}`); |
There was a problem hiding this comment.
🟠 MAJOR: URL state pattern diverges from established convention
Issue: This uses window.history.replaceState for tab URL updates, while analogous components (e.g., evaluations-tabs.tsx, dataset-tabs.tsx) use router.replace().
Why: The performance benefit is real (avoids Next.js router overhead), but this creates a split-world where two patterns coexist for the same use case. Future contributors won't know which pattern to follow. Additionally, window.history.replaceState bypasses Next.js navigation state management entirely — any middleware, analytics, or navigation listeners tied to the router won't fire.
Fix: Either:
- Keep this pattern but document it as the new standard and plan to migrate other tabs components
- Use
router.replace()to match the established convention:
const router = useRouter();
router.replace(`${pathname}?${params.toString()}`, { scroll: false });Refs:
- evaluations-tabs.tsx:48 — uses
router.replace() - dataset-tabs.tsx:50 — uses
router.push()
There was a problem hiding this comment.
Fair point, but at the same time switching tabs shouldn't trigger another server round trip. The app becomes very slow at that point- would suggest applying this pattern more broadly.
There was a problem hiding this comment.
hmm I’m a bit worried that using window.history.replaceState instead of router.replace could cause future bugs — e.g. if another component starts using useSearchParams for the tab, it won’t update when the tab changes. Maybe we could at least add some comments around why this decision was made if there aren't any alternatives
There was a problem hiding this comment.
PR Review Summary
(0) New Issues | (5) Pending from Prior Review | Risk: Medium
Delta Review
This re-review covers only the changes since the previous automated review:
Commit: 26bf0c5c6 removed unused index
The delta is a single-line deletion of the barrel export file agents-manage-ui/src/components/project-triggers/index.ts:
- export { TriggersTabs } from './triggers-tabs';✅ Delta is clean. The page.tsx now imports TriggersTabs directly from the path (@/components/project-triggers/triggers-tabs), so the barrel export is no longer needed. No issues with this cleanup.
🕐 Pending Recommendations (5)
The following issues from the prior review remain unaddressed:
- 🟠
triggers-tabs.tsx:17Missing Suspense boundary for useSearchParams — entire page may bail out to CSR - 🟠
page.tsx:36-50Error handling regression — errors now show generic message instead of context-specific - 🟠
project-triggers.ts:51,78Redundant fetchAgents calls when both tabs load — consider Reactcache()wrapper - 🟠
triggers-tabs.tsx:31URL state pattern diverges from established convention (window.history.replaceStatevsrouter.replace()) - 🟡
scheduled-tab-panel.tsx:30-32Unnecessary useEffect to sync initialTriggers to state
🚫 REQUEST CHANGES
Summary: The delta commit (removing unused barrel export) is clean and introduces no new issues. However, the prior review raised 3 Major and 2 Minor issues that remain unaddressed. The most critical are: (1) missing Suspense boundary for useSearchParams which may cause the page to bail out to client-side rendering, defeating the optimization goal, and (2) error handling regression that degrades user experience when data fetching fails. Please address the pending recommendations before merging.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| None dispatched — delta too trivial | — | — | — | — | — | — | — |
Note: The delta was a single-line file deletion, so no reviewer subagents were dispatched. Prior findings carry forward as Pending Recommendations.
There was a problem hiding this comment.
PR Review Summary
(1) New Issue | (4) Pending from Prior Review | Risk: Medium
Delta Review
This re-review covers only the changes since the previous automated review:
Commit: 4fe29edaa removed unnecessary useeffect
The delta removes the unnecessary useEffect that synced initialTriggers to state — this addresses the prior Minor finding. ✅
However, the removal appears to have introduced an accidental artifact:
🟡 Minor (1) 🟡
Inline Comments:
- 🟡 Minor:
scheduled-tab-panel.tsx:37Stray2;dead code statement
🕐 Pending Recommendations (4)
The following issues from prior reviews remain unaddressed:
- 🟠
triggers-tabs.tsx:17Missing Suspense boundary for useSearchParams — page may bail out to CSR - 🟠
page.tsx:36-50Error handling regression — errors now show generic message instead of context-specific - 🟠
project-triggers.ts:51,78Redundant fetchAgents calls when both tabs load — consider Reactcache()wrapper - 🟠
triggers-tabs.tsx:31URL state pattern diverges from established convention
Re: URL state pattern — The author's response about router.replace() triggering server round trips is a valid concern. If this is the intended pattern going forward for performance-sensitive tab switches, consider:
- Documenting this as the new standard for tab URL state
- Planning migration of other tab components (evaluations-tabs, dataset-tabs) for consistency
💡 APPROVE WITH SUGGESTIONS
Summary: The delta commit successfully addresses the unnecessary useEffect finding from the prior review, but introduces a minor typo (2; dead code on line 37) that should be removed. The 4 pending Major/Minor issues from prior reviews remain — 3 are blocking concerns (Suspense boundary, error handling, redundant fetches) and 1 is a consistency question with valid pushback from the author. Once the stray 2; is removed and the pending items are considered, this PR is in good shape.
Discarded (0)
No items discarded in this delta review.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| None dispatched — delta trivial | — | — | — | — | — | — | — |
Note: The delta was a 5-line change in a single file; no reviewer subagents were dispatched. The stray 2; was identified during direct orchestrator analysis.
| setTriggers(updatedTriggers); | ||
| } catch (error) { | ||
| console.error('Failed to fetch scheduled triggers:', error); | ||
| 2; |
There was a problem hiding this comment.
🟡 Minor: Stray dead code statement
Issue: This line contains a stray 2; statement that does nothing — appears to be an accidental edit artifact from removing the useEffect.
Why: Dead code confuses future readers and may trigger linter warnings.
Fix:
| 2; |
Refs:
- Introduced in commit
4fe29edaa("removed unnecessary useeffect")
There was a problem hiding this comment.
PR Review Summary
(0) New Issues | (3) Pending from Prior Review | Risk: Medium
Delta Review
This re-review covers only the changes since the previous automated review:
Commit: c85531d33 removed stray 2
The delta removes the stray 2; dead code statement — this addresses the prior Minor finding. ✅
🕐 Pending Recommendations (3)
The following issues from prior reviews remain unaddressed:
- 🟠
triggers-tabs.tsx:17Missing Suspense boundary for useSearchParams — page may bail out to CSR - 🟠
page.tsx:36-50Error handling regression — errors now show generic message instead of context-specific - 🟠
project-triggers.ts:51,78Redundant fetchAgents calls when both tabs load — consider Reactcache()wrapper
Note on URL state pattern: The author's response about router.replace() triggering server round trips is valid and accepted. The window.history.replaceState approach is a reasonable performance optimization for tab switching. If this becomes the standard pattern, consider documenting it for future contributors and migrating other tab components for consistency.
💡 APPROVE WITH SUGGESTIONS
Summary: The delta commit successfully addresses the stray 2; dead code from the prior review — nice cleanup! 🎉 There are still 3 pending Major recommendations from the original review that are worth considering before merge: (1) wrapping TriggersTabs in a Suspense boundary for useSearchParams, (2) adding error boundaries for better error messaging, and (3) using React cache() to dedupe fetchAgents calls. These are all incremental improvements that could be addressed in follow-up work if preferred.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| None dispatched — delta trivial | — | — | — | — | — | — | — |
Note: The delta was a single-line deletion fixing a prior finding; no reviewer subagents were dispatched.
There was a problem hiding this comment.
PR Review Summary
(0) New Issues | (2) Pending from Prior Review | Risk: Low
Delta Review
This re-review covers only the changes since the previous automated review:
Commit: 3dc70f8ae added changeset
The delta adds a changeset file for the patch release:
---
"@inkeep/agents-manage-ui": patch
---
Fix trigger page performance issues re: initial page load and tab switching✅ Delta is clean. The changeset correctly describes the PR's intent and follows the expected format. No issues.
🕐 Pending Recommendations (2)
After re-examining the prior findings with fresh codebase exploration, I've reassessed each recommendation:
Still Valid:
-
🟠
project-triggers.ts:51,78Redundant fetchAgents calls when both tabs load- Both
fetchProjectTriggersandfetchProjectScheduledTriggersindependently callfetchAgents(tenantId, projectId) - Since the RSC content components execute in parallel via separate Suspense boundaries, this results in two identical API calls
- Fix: Add React's
cache()wrapper tofetchAgents(similar to howgetFullAgentis already wrapped at line 112)
- Both
-
🟡
page.tsx:36-50Error handling removed — now uses generic tenant error boundary- The previous implementation had try-catch with
FullPageError(context="triggers")providing context-specific messages - The new RSC pattern relies on the generic
[tenantId]/error.tsxboundary which shows "Something went wrong" - Impact: Lower-fidelity error messages when data fetching fails, but not a blocker
- The previous implementation had try-catch with
Closed/Accepted:
-
✅ URL state pattern (
window.history.replaceState) — Author's response about avoiding server round trips is valid. This is an acceptable performance optimization for tab switching. -
✅ Missing Suspense boundary for useSearchParams — After exploration, I confirmed the page uses
force-dynamic, so static rendering concerns don't apply. The streaming behavior is acceptable since the inner content components ARE wrapped in Suspense.
💡 APPROVE WITH SUGGESTIONS
Summary: The delta (changeset addition) is clean and the PR is ready to merge. 🎉 Two recommendations remain for consideration: (1) wrapping fetchAgents with React cache() to dedupe the parallel API calls, and (2) optionally adding error boundaries for more descriptive error messages. Neither is blocking — the performance optimization goal of the PR is achieved.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| None dispatched — delta trivial | — | — | — | — | — | — | — |
Note: The delta was a single changeset file addition; no reviewer subagents were dispatched. Prior findings were re-assessed based on fresh codebase exploration.
There was a problem hiding this comment.
PR Review Summary
(0) New Issues | (2) Pending from Prior Reviews | Risk: Low
Delta Review
This re-review covers only the changes since the previous automated review:
Commit: 4dd8d5433 Merge branch 'main' into trigger-ui-optimizations
The delta is a merge commit incorporating changes from main into the PR branch. The merged changes relate to tool approval denial reason propagation (PR #2500, #2520) and are unrelated to this PR's trigger page optimizations.
PR-specific files are unchanged:
new-trigger-dialog.tsx— no changesscheduled-tab-content.tsx— no changesscheduled-tab-panel.tsx— no changestriggers-tabs.tsx— no changeswebhooks-tab-content.tsx— no changeswebhooks-tab-panel.tsx— no changespage.tsx(triggers) — no changesproject-triggers.ts(API) — no changes
✅ Delta is clean. The merge brings the branch up to date with main without introducing new issues to the PR code.
🕐 Pending Recommendations (2)
The following items from prior reviews remain for consideration:
-
🟠
project-triggers.ts:51,78Redundant fetchAgents calls — BothfetchProjectTriggersandfetchProjectScheduledTriggersindependently callfetchAgents(). With parallel Suspense boundaries, this results in duplicate API calls. Consider wrappingfetchAgentswith Reactcache()(similar to howgetFullAgentis wrapped). -
🟡
new-trigger-dialog.tsx:63Dialog cancel doesn't reset agent selection state — The Cancel button closes the dialog but doesn't resetselectedAgent, causing stale state on reopen. (Identified by Ito QA - EDGE-6). See inline comment for fix.
🟡 Minor (1) 🟡
Inline Comments:
- 🟡 Minor:
new-trigger-dialog.tsx:63Dialog cancel doesn't reset agent selection state
✅ APPROVE
Summary: The delta (merge from main) is clean and introduces no new issues to the PR code. 🎉 The PR achieves its stated goal of fixing trigger page performance issues for initial load and tab switching. Two items remain for consideration: (1) wrapping fetchAgents with React cache() to dedupe parallel API calls (optimization), and (2) resetting dialog state on cancel (UX bug from Ito QA). Neither is blocking — they can be addressed in follow-up work if preferred.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| None dispatched — merge-only delta | — | — | — | — | — | — | — |
Note: The delta was a merge commit with no PR-specific code changes; no reviewer subagents were dispatched. Prior findings carry forward as Pending Recommendations.
| className="w-[var(--radix-popover-trigger-width)]" | ||
| /> | ||
| <div className="flex justify-end gap-2"> | ||
| <Button variant="outline" onClick={() => setOpen(false)}> |
There was a problem hiding this comment.
🟡 Minor: Dialog cancel doesn't reset agent selection state
Issue: The Cancel button only closes the dialog via setOpen(false) but doesn't reset selectedAgent state. If a user selects an agent, cancels, and reopens the dialog, the previous selection persists.
Why: This creates confusing UX where the dialog remembers stale state across open/close cycles. Users expect a fresh dialog when reopening. (Identified by Ito QA testing - EDGE-6)
Fix:
| <Button variant="outline" onClick={() => setOpen(false)}> | |
| <Button variant="outline" onClick={() => { setSelectedAgent(''); setOpen(false); }}> |
Alternatively, reset state in onOpenChange:
<Dialog open={open} onOpenChange={(isOpen) => {
if (!isOpen) setSelectedAgent('');
setOpen(isOpen);
}}>Refs:
|
@claude verify and add a changeset |
|
Claude finished @dimaMachina's task in 1m 28s —— View job Verify and Add Changeset
SummaryA changeset already existed (
Commit: |
Co-authored-by: Dimitri POSTOLOV <[email protected]>
There was a problem hiding this comment.
PR Review Summary
(0) New Issues | (2) Pending from Prior Reviews | Risk: Low
Delta Review
This re-review covers only the changes since the previous automated review:
Commit: d16aa2caf polish
The delta refactors the composition pattern:
page.tsx— MovesTabsContentfromTriggersTabsto the page, nesting them as childrentriggers-tabs.tsx— Simplified to acceptchildrenprop instead of separatescheduledContent/webhooksContentprops
✅ Delta is clean. This is a good architectural refinement. By moving TabsContent to the page level (inside the RSC), the Suspense boundaries wrap the actual content components directly where they're defined. This pattern:
- Improves composition flexibility
- Makes the page structure more explicit
- Aligns with React Server Component best practices where async content boundaries are clearer at the page level
🟡 Minor (1) 🟡
Inline Comments:
- 🟡 Minor:
new-trigger-dialog.tsx:63Dialog cancel doesn't reset agent selection state
🕐 Pending Recommendations (2)
The following items from prior reviews remain for consideration:
-
🟠
project-triggers.ts:51,78Redundant fetchAgents calls — BothfetchProjectTriggersandfetchProjectScheduledTriggersindependently callfetchAgents(). With parallel Suspense boundaries, this results in duplicate API calls. Consider wrappingfetchAgentswith Reactcache()(similar to howgetFullAgentis wrapped at line 112). -
🟡
new-trigger-dialog.tsx:63Dialog cancel doesn't reset agent selection state — See inline comment for fix. (Identified by Ito QA - EDGE-6)
✅ APPROVE
Summary: The delta (composition pattern refactor) is clean and improves the page structure. 🎉 The PR achieves its stated goal of fixing trigger page performance issues for initial load and tab switching. Two items remain for consideration: (1) wrapping fetchAgents with React cache() to dedupe parallel API calls, and (2) resetting dialog state on cancel. Neither is blocking — they can be addressed in follow-up work if preferred.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| None dispatched — delta trivial | — | — | — | — | — | — | — |
Note: The delta was a composition pattern refactor (14 additions, 25 deletions across 2 files); no reviewer subagents were dispatched. Prior findings carry forward as Pending Recommendations.
| className="w-[var(--radix-popover-trigger-width)]" | ||
| /> | ||
| <div className="flex justify-end gap-2"> | ||
| <Button variant="outline" onClick={() => setOpen(false)}> |
There was a problem hiding this comment.
🟡 Minor: Dialog cancel doesn't reset agent selection state
Issue: The Cancel button only closes the dialog via setOpen(false) but doesn't reset selectedAgent state. If a user selects an agent, cancels, and reopens the dialog, the previous selection persists.
Why: This creates confusing UX where the dialog remembers stale state across open/close cycles. Users expect a fresh dialog when reopening. (Identified by Ito QA testing - EDGE-6)
Fix:
| <Button variant="outline" onClick={() => setOpen(false)}> | |
| <Button variant="outline" onClick={() => { setSelectedAgent(''); setOpen(false); }}> |
Alternatively, reset state in onOpenChange:
<Dialog open={open} onOpenChange={(isOpen) => {
if (!isOpen) setSelectedAgent('');
setOpen(isOpen);
}}>Refs:
There was a problem hiding this comment.
PR Review Summary
(0) New Issues | Risk: Low
Delta Review
This re-review covers only the changes since the previous automated review:
Commits:
9e1f76265— fix: reset dialog agent selection on close, improve changeset messagecbd0689dc— dedupe fetchAgents req
✅ Both Pending Issues Resolved
1. Dialog state reset bug (EDGE-6) — Fixed
The NewTriggerDialog now resets selectedAgent when the dialog closes via onOpenChange:
<Dialog
open={open}
onOpenChange={(isOpen) => {
if (!isOpen) setSelectedAgent('');
setOpen(isOpen);
}}
>This correctly handles all close paths (Cancel button, overlay click, Escape key) by resetting state in the onOpenChange callback rather than only on Cancel click. ✅
2. Redundant fetchAgents calls — Fixed
The fetchAgents function is now wrapped with React's cache():
async function $fetchAgents(tenantId: string, projectId: string): Promise<ListResponse<Agent>> {
// ... implementation
}
export const fetchAgents = cache($fetchAgents);This follows the existing pattern used by getFullAgent (line 111) and ensures that when both fetchProjectTriggers and fetchProjectScheduledTriggers call fetchAgents() in parallel Suspense boundaries, the request is deduplicated within the same render tree. ✅
3. Changeset improved — Good
The changeset message was updated from generic "Fix trigger page performance issues" to a more descriptive message explaining the RSC/Suspense/nuqs approach, per repo guidelines.
✅ APPROVE
Summary: The delta successfully addresses both pending issues from the prior review — the dialog state reset bug identified by Ito QA and the redundant fetchAgents API calls. Both fixes follow established codebase patterns and are correctly implemented. 🎉
The PR is ready to merge. It achieves its stated goal of improving trigger page performance by:
- Splitting tab content into separate RSC components with Suspense boundaries
- Using
nuqsfor client-side tab switching (no server round trips) - Deduplicating
fetchAgentscalls with Reactcache()
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| None dispatched — delta trivial | — | — | — | — | — | — | — |
Note: The delta was 2 commits addressing previously identified issues; no reviewer subagents were dispatched.
There was a problem hiding this comment.
PR Review Summary
(0) New Issues | Risk: Low
Delta Review
This re-review covers only the changes since the previous automated review:
Commit: 141931bce use dialog.close
The delta refactors NewTriggerDialog to use idiomatic Radix patterns:
-import { useState } from 'react';
+import { DialogClose } from '@/components/ui/dialog';
...
-const [open, setOpen] = useState(false);
-<Dialog open={open} onOpenChange={(isOpen) => { if (!isOpen) setSelectedAgent(''); setOpen(isOpen); }}>
+<Dialog onOpenChange={(isOpen) => { if (!isOpen) setSelectedAgent(''); }}>
...
-<Button variant="outline" onClick={() => setOpen(false)}>Cancel</Button>
+<DialogClose asChild><Button variant="outline">Cancel</Button></DialogClose>✅ Delta is clean and improves the code. This change:
- Simplifies the dialog by switching from controlled to uncontrolled mode
- Uses Radix's
DialogClosecomponent for the Cancel button (idiomatic pattern) - Preserves the EDGE-6 bug fix —
selectedAgentis still reset viaonOpenChangewhen the dialog closes - Minor CSS improvement:
w-(--radix-popover-trigger-width)is the modern Tailwind CSS syntax for CSS variables
✅ APPROVE
Summary: The delta is a clean refactor that simplifies the dialog implementation by using Radix's uncontrolled mode and DialogClose component. All previously identified issues remain resolved. The PR achieves its stated goal of improving trigger page performance through RSC/Suspense patterns and is ready to merge. 🎉
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| None dispatched — delta trivial | — | — | — | — | — | — | — |
Note: The delta was a single-file refactor (8 additions, 9 deletions) simplifying the dialog pattern; no reviewer subagents were dispatched.
PR description -> https://inkeep.slack.com/archives/C09020FHAFK/p1771828515751939