@@ -81,16 +100,7 @@ export function ListsIndex({ lists }: { lists: ListDto[] }) {
{groupLists.map((list) => (
-
-
{list.name}
-
- {list.openCount} open / {list.doneCount} done
-
-
+
))}
@@ -99,3 +109,76 @@ export function ListsIndex({ lists }: { lists: ListDto[] }) {
);
}
+
+function ListCard({
+ list,
+ onToggle,
+}: {
+ list: ListWithItemsDto;
+ onToggle: (listId: string, item: ListIndexItem, done: boolean) => void;
+}) {
+ const [expanded, setExpanded] = useState(true);
+
+ return (
+
+
+
+
+
{list.name}
+
+ {list.openCount} open · {list.doneCount} done
+
+
+
+
+
+
+
+ {expanded && (
+
+ {list.items.length === 0 ? (
+
+ {list.openCount === 0 ? "All done!" : "No items to show."}
+
+ ) : (
+
+ )}
+
+ )}
+
+ );
+}
diff --git a/src/modules/lists/manifest.tsx b/src/modules/lists/manifest.tsx
index 3b28468..3b2d626 100644
--- a/src/modules/lists/manifest.tsx
+++ b/src/modules/lists/manifest.tsx
@@ -3,6 +3,7 @@ import { z } from "zod";
import { listLists, listWidgetItems, searchItems, searchLists } from "./server/queries";
import { loadListForShare, type ListShareData } from "./server/share-queries";
import { ListSharedView } from "./components/shared-view";
+import { ListWidget } from "./components/list-widget";
const listIdsSchema = z.union([z.literal("all"), z.array(z.string().uuid())]);
@@ -12,37 +13,14 @@ const listWidgetConfigSchema = z.object({
limit: z.number().int().min(1).max(50).optional(),
});
-async function ListWidget({ config }: { config: unknown; ctx: WidgetContext }) {
+async function ListWidgetServer({ config }: { config: unknown; ctx: WidgetContext }) {
const parsed = listWidgetConfigSchema.parse(config);
const items = await listWidgetItems({
listIds: parsed.listIds,
showCompleted: parsed.showCompleted,
limit: parsed.limit ?? 10,
});
-
- if (items.length === 0) {
- return (
-
- {parsed.showCompleted ? "No items" : "No open items"}
-
- );
- }
-
- return (
-
- {items.map((item) => (
- -
-
-
- {item.text}
-
- {item.listName}
-
- ))}
-
- );
+ return
;
}
const manifest: ModuleManifest = {
@@ -109,7 +87,7 @@ const manifest: ModuleManifest = {
name: list.name,
})),
}),
- render: (props) =>
,
+ render: (props) =>
,
},
],
quickAdds: [
diff --git a/src/modules/lists/server/queries.ts b/src/modules/lists/server/queries.ts
index 010e893..d60f90f 100644
--- a/src/modules/lists/server/queries.ts
+++ b/src/modules/lists/server/queries.ts
@@ -156,6 +156,84 @@ export async function searchItems(query: string, householdId: string) {
}));
}
+export type ListIndexItem = {
+ id: string;
+ text: string;
+ done: boolean;
+ position: number;
+};
+
+export type ListWithItemsDto = ListDto & { items: ListIndexItem[] };
+
+export async function listListsWithItems(): Promise
{
+ const { household } = await getCurrentSession();
+ await ensureDefaultListsForHousehold(household.id);
+
+ const listsRows = await db
+ .select()
+ .from(lists)
+ .where(and(eq(lists.householdId, household.id), eq(lists.archived, false)))
+ .orderBy(asc(lists.type), asc(lists.name));
+
+ if (listsRows.length === 0) return [];
+
+ const listIds = listsRows.map((l) => l.id);
+
+ // Fetch all open items for these lists, ordered so we can take top 10 per list in JS
+ const itemRows = await db
+ .select({
+ id: listItems.id,
+ listId: listItems.listId,
+ text: listItems.text,
+ done: listItems.done,
+ position: listItems.position,
+ createdAt: listItems.createdAt,
+ })
+ .from(listItems)
+ .where(and(inArray(listItems.listId, listIds), eq(listItems.done, false)))
+ .orderBy(asc(listItems.position), asc(listItems.createdAt));
+
+ const itemsByList = new Map();
+ for (const item of itemRows) {
+ const existing = itemsByList.get(item.listId) ?? [];
+ if (existing.length < 10) {
+ existing.push({ id: item.id, text: item.text, done: item.done, position: item.position });
+ }
+ itemsByList.set(item.listId, existing);
+ }
+
+ // Count all items (open + done) per list
+ const countRows = await db
+ .select({
+ listId: listItems.listId,
+ done: listItems.done,
+ })
+ .from(listItems)
+ .where(inArray(listItems.listId, listIds));
+
+ const counts = new Map();
+ for (const row of countRows) {
+ const c = counts.get(row.listId) ?? { open: 0, done: 0 };
+ if (row.done) c.done += 1;
+ else c.open += 1;
+ counts.set(row.listId, c);
+ }
+
+ return listsRows.map((list) => {
+ const c = counts.get(list.id) ?? { open: 0, done: 0 };
+ return {
+ id: list.id,
+ type: list.type,
+ name: list.name,
+ archived: list.archived,
+ openCount: c.open,
+ doneCount: c.done,
+ createdAt: list.createdAt.toISOString(),
+ items: itemsByList.get(list.id) ?? [],
+ };
+ });
+}
+
export async function listWidgetItems(input: {
listIds: "all" | string[];
showCompleted: boolean;
@@ -184,6 +262,7 @@ export async function listWidgetItems(input: {
id: listItems.id,
text: listItems.text,
done: listItems.done,
+ listId: listItems.listId,
listName: lists.name,
})
.from(listItems)