diff --git a/src/components/shared/CreatedByFilter/CreatedByFilter.test.tsx b/src/components/shared/CreatedByFilter/CreatedByFilter.test.tsx
new file mode 100644
index 000000000..035b3666d
--- /dev/null
+++ b/src/components/shared/CreatedByFilter/CreatedByFilter.test.tsx
@@ -0,0 +1,146 @@
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { describe, expect, it, vi } from "vitest";
+
+import { CreatedByFilter } from "./CreatedByFilter";
+
+describe("CreatedByFilter", () => {
+ describe("rendering", () => {
+ it("should render toggle and search input", () => {
+ render();
+
+ expect(screen.getByRole("switch")).toBeInTheDocument();
+ expect(screen.getByLabelText("Created by me")).toBeInTheDocument();
+ expect(screen.getByPlaceholderText("Search by user")).toBeInTheDocument();
+ expect(screen.getByRole("button", { name: "Search" })).toBeInTheDocument();
+ });
+
+ it("should show 'Created by me' when no value", () => {
+ render();
+
+ expect(screen.getByLabelText("Created by me")).toBeInTheDocument();
+ });
+
+ it("should show 'Created by {user}' when value is set", () => {
+ render();
+
+ expect(screen.getByLabelText("Created by john.doe")).toBeInTheDocument();
+ });
+
+ it("should have switch unchecked when no value", () => {
+ render();
+
+ expect(screen.getByRole("switch")).not.toBeChecked();
+ });
+
+ it("should have switch checked when value is set", () => {
+ render();
+
+ expect(screen.getByRole("switch")).toBeChecked();
+ });
+ });
+
+ describe("toggle behavior", () => {
+ it("should call onChange with 'me' when toggle is turned on", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render();
+
+ await user.click(screen.getByRole("switch"));
+
+ expect(onChange).toHaveBeenCalledWith("me");
+ });
+
+ it("should call onChange with undefined when toggle is turned off", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render();
+
+ await user.click(screen.getByRole("switch"));
+
+ expect(onChange).toHaveBeenCalledWith(undefined);
+ });
+
+ it("should not change value when toggle on with existing value", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render();
+
+ // Toggle is already on, clicking it should turn it off
+ await user.click(screen.getByRole("switch"));
+
+ expect(onChange).toHaveBeenCalledWith(undefined);
+ });
+ });
+
+ describe("search behavior", () => {
+ it("should have search button disabled when input is empty", () => {
+ render();
+
+ expect(screen.getByRole("button", { name: "Search" })).toBeDisabled();
+ });
+
+ it("should enable search button when input has text", async () => {
+ const user = userEvent.setup();
+ render();
+
+ await user.type(screen.getByPlaceholderText("Search by user"), "jane");
+
+ expect(screen.getByRole("button", { name: "Search" })).toBeEnabled();
+ });
+
+ it("should call onChange with typed user when search clicked", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render();
+
+ await user.type(screen.getByPlaceholderText("Search by user"), "jane.doe");
+ await user.click(screen.getByRole("button", { name: "Search" }));
+
+ expect(onChange).toHaveBeenCalledWith("jane.doe");
+ });
+
+ it("should call onChange when Enter is pressed in input", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render();
+
+ const input = screen.getByPlaceholderText("Search by user");
+ await user.type(input, "jane.doe{Enter}");
+
+ expect(onChange).toHaveBeenCalledWith("jane.doe");
+ });
+
+ it("should trim whitespace from search input", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render();
+
+ await user.type(screen.getByPlaceholderText("Search by user"), " jane ");
+ await user.click(screen.getByRole("button", { name: "Search" }));
+
+ expect(onChange).toHaveBeenCalledWith("jane");
+ });
+
+ it("should not call onChange when searching with only whitespace", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render();
+
+ await user.type(screen.getByPlaceholderText("Search by user"), " ");
+
+ // Button should still be disabled
+ expect(screen.getByRole("button", { name: "Search" })).toBeDisabled();
+ });
+ });
+
+ describe("initial state", () => {
+ it("should populate search input with current value", () => {
+ render();
+
+ expect(screen.getByPlaceholderText("Search by user")).toHaveValue(
+ "existing-user",
+ );
+ });
+ });
+});
diff --git a/src/components/shared/CreatedByFilter/CreatedByFilter.tsx b/src/components/shared/CreatedByFilter/CreatedByFilter.tsx
new file mode 100644
index 000000000..dc0acd38a
--- /dev/null
+++ b/src/components/shared/CreatedByFilter/CreatedByFilter.tsx
@@ -0,0 +1,82 @@
+import { useState } from "react";
+
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { InlineStack } from "@/components/ui/layout";
+import { Switch } from "@/components/ui/switch";
+
+interface CreatedByFilterProps {
+ /** Current filter value. undefined means no filter, "me" means current user. */
+ value: string | undefined;
+ /** Called when the filter value changes. undefined clears the filter. */
+ onChange: (value: string | undefined) => void;
+}
+
+/**
+ * Filter component for filtering by creator/initiator.
+ * Provides a toggle for "Created by me" and an input for searching by specific user.
+ */
+export function CreatedByFilter({ value, onChange }: CreatedByFilterProps) {
+ const [searchUser, setSearchUser] = useState(value ?? "");
+
+ const isFilterActive = value !== undefined;
+ const toggleText = value ? `Created by ${value}` : "Created by me";
+
+ const handleToggleChange = (checked: boolean) => {
+ if (checked) {
+ // Enable filter - if no specific user set, default to "me"
+ if (!value) {
+ onChange("me");
+ setSearchUser("");
+ }
+ } else {
+ // Disable filter
+ onChange(undefined);
+ }
+ };
+
+ const handleUserSearch = () => {
+ const trimmedUser = searchUser.trim();
+ if (trimmedUser) {
+ onChange(trimmedUser);
+ }
+ };
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === "Enter" && searchUser.trim()) {
+ e.preventDefault();
+ handleUserSearch();
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ setSearchUser(e.target.value)}
+ onKeyDown={handleKeyDown}
+ className="w-40"
+ />
+
+
+
+ );
+}