Skip to content
Open
51 changes: 1 addition & 50 deletions desktop/src/apps/NotesApp.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render, screen, act, waitFor, within, fireEvent } from "@testing-library/react";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { NotesApp, TodoApp } from "./NotesApp";
import { NotesApp } from "./NotesApp";

function mockFetch(
resolver: (url: string, init?: RequestInit) => { ok: boolean; status?: number; body: unknown },
Expand Down Expand Up @@ -33,14 +33,6 @@ const noteDoc = {
archived_at: null,
};

const listDoc = {
id: "list-1",
kind: "list" as const,
title: "Sprint backlog",
updated_at: past(10),
archived_at: null,
};

describe("NotesApp", () => {
beforeEach(() => {
vi.restoreAllMocks();
Expand All @@ -62,23 +54,6 @@ describe("NotesApp", () => {
expect(screen.getByRole("button", { name: /create one/i })).toBeTruthy();
});

it("renders only notes, filtering out lists that share the same API", async () => {
vi.stubGlobal(
"fetch",
mockFetch(() => ({ ok: true, body: [noteDoc, listDoc] })),
);
render(<NotesApp windowId="w1" />);
await flush();

await waitFor(() =>
expect(screen.getByText("Project kickoff")).toBeTruthy(),
);
// The list doc lives in the same store but NotesApp must hide it.
expect(screen.queryByText("Sprint backlog")).toBeNull();
// The relative updated time is rendered from updated_at.
expect(screen.getByText(/30m ago/i)).toBeTruthy();
});

it("loads the selected note's detail via /api/notes/:id", async () => {
const fetchMock = mockFetch((url) => {
if (url === "/api/notes") {
Expand Down Expand Up @@ -178,27 +153,3 @@ describe("NotesApp", () => {
});
});

describe("TodoApp", () => {
beforeEach(() => {
vi.restoreAllMocks();
});

it("renders only lists, filtering out notes that share the same API", async () => {
vi.stubGlobal(
"fetch",
mockFetch(() => ({ ok: true, body: [noteDoc, listDoc] })),
);
render(<TodoApp windowId="w2" />);
await flush();

await waitFor(() => expect(screen.getByText("Sprint backlog")).toBeTruthy());
expect(screen.queryByText("Project kickoff")).toBeNull();
});

it("shows the todo empty state when there are no lists", async () => {
vi.stubGlobal("fetch", mockFetch(() => ({ ok: true, body: [] })));
render(<TodoApp windowId="w2" />);
await flush();
await waitFor(() => expect(screen.getByText(/no lists yet/i)).toBeTruthy());
});
});
31 changes: 6 additions & 25 deletions desktop/src/apps/NotesApp.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useState, useEffect, useCallback, useRef } from "react";
import {
StickyNote,
ListChecks,
Plus,
Clock,
Users,
Expand All @@ -23,7 +22,7 @@ import { Button, Textarea } from "@/components/ui";

interface NoteDoc {
id: string;
kind: "note" | "list";
kind: "note";
title: string;
updated_at: string;
archived_at: string | null;
Expand All @@ -47,7 +46,7 @@ interface NoteMember {

interface NoteDetail {
id: string;
kind: "note" | "list";
kind: "note";
title: string;
updated_at: string;
entries: NoteEntry[];
Expand Down Expand Up @@ -89,12 +88,11 @@ const ACTION_OPTIONS = [
] as const;

// ---- Kind config ----
// Notes and lists share one component, one store, and one API; they differ only
// in copy, icon, and whether entries carry a done checkbox. A config object
// keeps both variants in sync instead of forking ~1000 lines.
// NotesApp is now notes-only (todos live in TodoApp). The config object
// keeps copy and behaviour centralised instead of scattering magic strings.

interface DocKindConfig {
kind: "note" | "list";
kind: "note";
appName: string; // header + listing aria
icon: LucideIcon;
noun: string; // doc-level aria: New {noun}, Create {noun}, Share {noun}
Expand All @@ -119,19 +117,6 @@ const NOTES_CONFIG: DocKindConfig = {
showDone: false,
};

const TODO_CONFIG: DocKindConfig = {
kind: "list",
appName: "Todo",
icon: ListChecks,
noun: "list",
titlePlaceholder: "List title...",
addPlaceholder: "Add a task...",
emptyDocs: "No lists yet.",
emptyEntries: "Nothing here yet. Add your first task above.",
selectPrompt: "Select a list to get started.",
showDone: true,
};

// ---- Sub-components ----

function MemberBadge({ member }: { member: NoteMember }) {
Expand Down Expand Up @@ -334,7 +319,7 @@ function SharePanel({ doc, onClose }: { doc: NoteDetail; onClose: () => void })
<div
className="flex flex-col gap-4 rounded-xl border border-shell-border bg-shell-bg-deep p-4"
role="dialog"
aria-label={`Share ${doc.kind === "list" ? "list" : "note"}`}
aria-label="Share note"
>
<div className="flex items-center justify-between">
<span className="flex items-center gap-1.5 text-sm font-semibold text-shell-text">
Expand Down Expand Up @@ -1171,7 +1156,3 @@ function DocsApp({ config }: { config: DocKindConfig }) {
export function NotesApp({ windowId: _windowId }: { windowId: string }) {
return <DocsApp config={NOTES_CONFIG} />;
}

export function TodoApp({ windowId: _windowId }: { windowId: string }) {
return <DocsApp config={TODO_CONFIG} />;
}
Loading
Loading