113 lines
3.8 KiB
TypeScript
113 lines
3.8 KiB
TypeScript
import type { ModuleManifest, WidgetContext } from "../_core/module";
|
|
import { z } from "zod";
|
|
import { listLists, listWidgetItems, searchItems, searchLists } from "./server/queries";
|
|
import { canShareList, 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())]);
|
|
|
|
const listWidgetConfigSchema = z.object({
|
|
listIds: listIdsSchema,
|
|
showCompleted: z.boolean(),
|
|
limit: z.number().int().min(1).max(50).optional(),
|
|
});
|
|
|
|
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,
|
|
});
|
|
return <ListWidget initialItems={items} />;
|
|
}
|
|
|
|
const manifest: ModuleManifest = {
|
|
id: "lists",
|
|
name: "Lists",
|
|
nav: { href: "/lists", label: "Lists", icon: "list" },
|
|
entities: [
|
|
{
|
|
type: "lists.list",
|
|
label: { singular: "List", plural: "Lists" },
|
|
share: { canShare: true, defaultCapabilities: ["read", "write"] },
|
|
search: { search: searchLists },
|
|
resolveUrl: (id) => `/lists/${id}`,
|
|
canShareEntity: canShareList,
|
|
loadForShare: loadListForShare,
|
|
renderSharedView: ({ data, capabilities, token }) => (
|
|
<ListSharedView data={data as ListShareData} canWrite={capabilities.write} token={token} />
|
|
),
|
|
renderActivity: (entry) => {
|
|
const name = entry.payload?.name as string | undefined;
|
|
if (entry.action === "create") return `Created list${name ? ` "${name}"` : ""}`;
|
|
if (entry.action === "archive") return `Archived list${name ? ` "${name}"` : ""}`;
|
|
if (entry.action === "delete") return "Deleted list";
|
|
return `Updated list${name ? ` "${name}"` : ""}`;
|
|
},
|
|
},
|
|
{
|
|
type: "lists.item",
|
|
label: { singular: "List item", plural: "List items" },
|
|
share: { canShare: false },
|
|
search: { search: searchItems },
|
|
resolveUrl: (id) => `/lists/items/${id}`,
|
|
renderActivity: (entry) => {
|
|
const text = entry.payload?.text as string | undefined;
|
|
if (entry.action === "create") return `Added${text ? ` "${text}"` : " item"}`;
|
|
if (entry.action === "delete") return `Removed${text ? ` "${text}"` : " item"}`;
|
|
if (entry.action === "toggle") {
|
|
const done = entry.payload?.done as boolean | undefined;
|
|
const label = text ? ` "${text}"` : " item";
|
|
return done ? `Checked off${label}` : `Unchecked${label}`;
|
|
}
|
|
return `Updated${text ? ` "${text}"` : " item"}`;
|
|
},
|
|
},
|
|
],
|
|
dashboardWidgets: [
|
|
{
|
|
id: "lists.list",
|
|
title: "List items",
|
|
description: "Open or completed items from selected lists.",
|
|
category: "Lists",
|
|
defaultSize: { w: 4, h: 3 },
|
|
minSize: { w: 3, h: 2 },
|
|
defaultPriority: 30,
|
|
configSchema: listWidgetConfigSchema,
|
|
defaultConfig: { listIds: "all", showCompleted: false },
|
|
resolveConfigOptions: async () => ({
|
|
lists: (await listLists()).map((list) => ({
|
|
id: list.id,
|
|
type: list.type,
|
|
name: list.name,
|
|
})),
|
|
}),
|
|
render: (props) => <ListWidgetServer {...props} />,
|
|
},
|
|
],
|
|
quickAdds: [
|
|
{
|
|
id: "lists.add-shopping",
|
|
label: "Add to shopping",
|
|
icon: "shopping-cart",
|
|
url: "/lists",
|
|
},
|
|
{
|
|
id: "lists.add-task",
|
|
label: "Add to tasks",
|
|
icon: "list-checks",
|
|
url: "/lists",
|
|
},
|
|
{
|
|
id: "lists.new-list",
|
|
label: "New list",
|
|
icon: "list-plus",
|
|
url: "/lists",
|
|
},
|
|
],
|
|
};
|
|
|
|
export default manifest;
|