Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions web/src/pages/merge-queue/columns.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { type ColumnDef } from "@tanstack/react-table";
import { Link } from "react-router-dom";
import { ExternalLink, Check, X } from "lucide-react";
import { ExternalLink, Check, X, MessageSquare } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { formatRelativeTime, projectLabel } from "@/lib/utils";
import { approveMerge, rejectMerge } from "@/lib/api";
import { approveMerge, rejectMerge, requestChanges } from "@/lib/api";
import type { MergeQueueEntry, MergeStatus, Project, Task } from "@/lib/types";

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -280,6 +280,13 @@ export const columns: ColumnDef<MergeQueueEntry>[] = [
refresh?.();
}

async function handleRequestChanges() {
const feedback = window.prompt("Describe the changes needed:");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]

Priority: Code Quality

The columns export (TanStack Table column definitions) is not imported by any active consumer — page.tsx only imports utility functions from this file (statusBadge, lifecyclePhases, etc.), not the columns array. This means handleRequestChanges using window.prompt() is dead code and will never run in the current UI.

If the table view is intended to be revived, this implementation has two problems compared to the page.tsx dialog approach: it uses a blocking native browser prompt (can be suppressed by browsers, unstyled, inconsistent UX) and it has no error handling — if requestChanges() throws, the error propagates silently.

Consider either wiring up the columns export to a table view, or removing the handleRequestChanges from columns.tsx entirely since the dialog in page.tsx handles this correctly.

if (!feedback?.trim()) return;
await requestChanges(entry.id, feedback.trim(), feedback.trim());
refresh?.();
}

return (
<div className="inline-flex items-center rounded-md border border-border">
<Button
Expand All @@ -291,6 +298,15 @@ export const columns: ColumnDef<MergeQueueEntry>[] = [
<Check className="h-3.5 w-3.5" />
Approve
</Button>
<Button
variant="ghost"
size="sm"
className="h-7 gap-1 rounded-none border-r border-border"
onClick={handleRequestChanges}
>
<MessageSquare className="h-3.5 w-3.5" />
Request Changes
</Button>
<Button
variant="ghost"
size="sm"
Expand Down
74 changes: 72 additions & 2 deletions web/src/pages/merge-queue/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { useMemo, useState } from "react";
import { Link } from "react-router-dom";
import { ExternalLink, Check, X, Info } from "lucide-react";
import { ExternalLink, Check, X, Info, MessageSquare } from "lucide-react";
import { toast } from "sonner";
import { useAppState } from "@/hooks/use-app-state";
import { flushMergeQueue, approveMerge, rejectMerge } from "@/lib/api";
import { flushMergeQueue, approveMerge, rejectMerge, requestChanges } from "@/lib/api";
import { formatRelativeTime, projectLabel } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { ListView, ListEmptyState } from "@/components/ui/list-view";
import { ListHeader, ListHeaderTabs } from "@/components/ui/list-header";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
ListRow,
LinkCell,
Expand Down Expand Up @@ -84,6 +93,25 @@ function MergeQueueRow({
}
}

const [requestChangesOpen, setRequestChangesOpen] = useState(false);
const [feedback, setFeedback] = useState("");
const [submitting, setSubmitting] = useState(false);

async function handleRequestChanges() {
if (!feedback.trim()) return;
setSubmitting(true);
try {
await requestChanges(entry.id, feedback.trim(), feedback.trim());
setRequestChangesOpen(false);
setFeedback("");
onRefresh();
} catch {
toast.error("Failed to request changes");
} finally {
setSubmitting(false);
}
}

return (
<ListRow>
{/* PR number */}
Expand Down Expand Up @@ -161,6 +189,48 @@ function MergeQueueRow({
<Check className="h-3.5 w-3.5" />
Approve
</Button>
<Dialog open={requestChangesOpen} onOpenChange={setRequestChangesOpen}>
<DialogTrigger asChild>
<Button
variant="ghost"
size="sm"
className="h-7 gap-1 rounded-none border-r border-border"
>
<MessageSquare className="h-3.5 w-3.5" />
Request Changes
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Request Changes</DialogTitle>
<DialogDescription>
Describe the changes needed. This feedback will be sent back to the agent.
</DialogDescription>
</DialogHeader>
<textarea
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring min-h-[100px] resize-y"
placeholder="Describe what needs to change..."
value={feedback}
onChange={(e) => setFeedback(e.target.value)}
/>
<DialogFooter>
<Button
variant="outline"
size="sm"
onClick={() => setRequestChangesOpen(false)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]

Priority: Code Quality

The Cancel button closes the dialog but doesn't reset the feedback state:

onClick={() => setRequestChangesOpen(false)}

If a user types some feedback, cancels, then reopens the dialog for the same entry, the previous draft text is still present. The success path correctly clears feedback (setFeedback("")), so the Cancel path is inconsistent with that expectation.

Consider resetting on close: onClick={() => { setRequestChangesOpen(false); setFeedback(""); }}. Similarly, you could add a cleanup to onOpenChange: onOpenChange={(open) => { if (!open) setFeedback(""); setRequestChangesOpen(open); }}.

>
Cancel
</Button>
<Button
size="sm"
onClick={handleRequestChanges}
disabled={!feedback.trim() || submitting}
>
{submitting ? "Submitting..." : "Submit"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Button
variant="ghost"
size="sm"
Expand Down
Loading