diff --git a/apps/web/app/(dashboard)/agents/page.tsx b/apps/web/app/(dashboard)/agents/page.tsx
index 502fc7ec..70625205 100644
--- a/apps/web/app/(dashboard)/agents/page.tsx
+++ b/apps/web/app/(dashboard)/agents/page.tsx
@@ -62,6 +62,7 @@ import {
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
+import { toast } from "sonner";
import { api } from "@/shared/api";
import { useAuthStore } from "@/features/auth";
import { useWorkspaceStore } from "@/features/workspace";
@@ -145,7 +146,8 @@ function CreateAgentDialog({
triggers: [{ id: generateId(), type: "on_assign", enabled: true, config: {} }],
});
onClose();
- } catch {
+ } catch (err) {
+ toast.error(err instanceof Error ? err.message : "Failed to create agent");
setCreating(false);
}
};
diff --git a/apps/web/app/(dashboard)/issues/[id]/page.test.tsx b/apps/web/app/(dashboard)/issues/[id]/page.test.tsx
index 86847a30..b6b6b151 100644
--- a/apps/web/app/(dashboard)/issues/[id]/page.test.tsx
+++ b/apps/web/app/(dashboard)/issues/[id]/page.test.tsx
@@ -61,6 +61,35 @@ vi.mock("@/features/workspace", () => ({
}),
}));
+// Mock issue store — supply a stable full issue object so storeIssue
+// doesn't create a new reference each render (avoids infinite effect loop)
+// and has all required fields for rendering.
+const stableStoreIssues = vi.hoisted(() => [
+ {
+ id: "issue-1",
+ workspace_id: "ws-1",
+ number: 1,
+ identifier: "TES-1",
+ title: "Implement authentication",
+ description: "Add JWT auth to the backend",
+ status: "in_progress",
+ priority: "high",
+ assignee_type: "member",
+ assignee_id: "user-1",
+ creator_type: "member",
+ creator_id: "user-1",
+ parent_issue_id: null,
+ position: 0,
+ due_date: "2026-06-01T00:00:00Z",
+ created_at: "2026-01-15T00:00:00Z",
+ updated_at: "2026-01-20T00:00:00Z",
+ },
+]);
+vi.mock("@/features/issues", () => ({
+ useIssueStore: (selector: (s: any) => any) =>
+ selector({ issues: stableStoreIssues }),
+}));
+
// Mock ws-context
vi.mock("@/features/realtime", () => ({
useWSEvent: () => {},
@@ -246,7 +275,7 @@ describe("IssueDetailPage", () => {
await renderPage("nonexistent-id");
await waitFor(() => {
- expect(screen.getByText("Issue not found")).toBeInTheDocument();
+ expect(screen.getByText("This issue does not exist or has been deleted in this workspace.")).toBeInTheDocument();
});
});
diff --git a/apps/web/features/issues/components/board-card.tsx b/apps/web/features/issues/components/board-card.tsx
index d418b103..a6eed5e0 100644
--- a/apps/web/features/issues/components/board-card.tsx
+++ b/apps/web/features/issues/components/board-card.tsx
@@ -91,7 +91,7 @@ export function BoardCardContent({
{/* Title */}
{issue.title}
diff --git a/apps/web/features/issues/components/comment-card.tsx b/apps/web/features/issues/components/comment-card.tsx
index a8ea065a..5db099fb 100644
--- a/apps/web/features/issues/components/comment-card.tsx
+++ b/apps/web/features/issues/components/comment-card.tsx
@@ -168,7 +168,7 @@ function CommentCard({
collectReplies(entry.id);
return (
-
+
{/* Parent comment */}
{
- const content = editorRef.current?.getMarkdown()?.trim();
+ const content = editorRef.current?.getMarkdown()?.replace(/(\n\s*)+$/, "").trim();
if (!content || submitting) return;
setSubmitting(true);
try {
@@ -28,8 +28,8 @@ function CommentInput({ onSubmit }: CommentInputProps) {
};
return (
-
-
+
+
-
+