diff --git a/apps/web/src/components/TodoPanel.tsx b/apps/web/src/components/TodoPanel.tsx
index ac4bf988b857..930736d2dea7 100644
--- a/apps/web/src/components/TodoPanel.tsx
+++ b/apps/web/src/components/TodoPanel.tsx
@@ -233,7 +233,13 @@ export function TodoPanel() {
{loading &&
Loading...
}
{error &&
{filteredCategories.map((category) => (
@@ -378,12 +384,14 @@ function SortableCategoryHeader({
function SortableTodoItem({
item,
+ categoryColor,
categoryJiraLink,
onCycleItem,
onSelectItem,
mutate,
}: {
item: TodoItem;
+ categoryColor: string;
categoryJiraLink: string | undefined;
onCycleItem: (itemId: string) => void;
onSelectItem: (itemId: string) => void;
@@ -453,7 +461,7 @@ function SortableTodoItem({
return (
@@ -540,9 +548,8 @@ function TodoCategoryRow({
const commitAdd = () => {
const trimmed = addValue.trim();
- if (trimmed) {
- mutate([{ type: "createItem", categoryId: category.id, title: trimmed }]);
- }
+ if (!trimmed) return;
+ mutate([{ type: "createItem", categoryId: category.id, title: trimmed }]);
setAdding(false);
setAddValue("");
};
@@ -682,6 +689,7 @@ function TodoCategoryRow({
{
const created = next.items.find((i) => i.categoryId === "cat-1");
expect(created!.sortOrder).toBe(0);
});
+
+ it("rejects empty title and returns unchanged state", () => {
+ const state = loadTodos(sampleCategories, sampleItems);
+ const next = createItem(state, "cat-1", "");
+
+ expect(next).toBe(state);
+ expect(next.items).toHaveLength(2);
+ });
+
+ it("rejects whitespace-only title and returns unchanged state", () => {
+ const state = loadTodos(sampleCategories, sampleItems);
+ const next = createItem(state, "cat-1", " ");
+
+ expect(next).toBe(state);
+ expect(next.items).toHaveLength(2);
+ });
+
+ it("rejects whitespace-only title through applyMutation", () => {
+ const state = loadTodos(sampleCategories, sampleItems);
+ const next = applyMutation(state, {
+ type: "createItem",
+ categoryId: "cat-1",
+ title: " ",
+ });
+
+ expect(next).toBe(state);
+ expect(next.items).toHaveLength(2);
+ });
});
describe("updateDescription", () => {
diff --git a/packages/shared/src/todoStore.ts b/packages/shared/src/todoStore.ts
index 91cb9d9faf35..6abbecc74903 100644
--- a/packages/shared/src/todoStore.ts
+++ b/packages/shared/src/todoStore.ts
@@ -114,6 +114,8 @@ export function deleteCategory(
}
export function createItem(state: TodoState, categoryId: string, title: string): TodoState {
+ if (!title.trim()) return state;
+
// @effect-diagnostics-next-line globalDate:off
const now = new Date().toISOString();
const categoryItems = state.items.filter((item) => item.categoryId === categoryId);