- Makes actorId nullable in activity_log (migration 0010) with onDelete: set null so audit history survives user deletion; adds logShareActivity for anonymous mutations - Adds resolveShareToken return of householdId for downstream validation - Adds renderSharedView to EntityTypeRegistration; each module implements loadForShare (no-session bare queries) and renderSharedView - Calendar: CalendarSharedView (upcoming 90-day events) and EventSharedView (title/time/location/notes) - Lists: ListSharedView (client component with optimistic toggles via toggleShareListItem server action in lists/server/share-actions.ts) - Notes: NoteSharedView (title + body + updated date) - /app/s/[token]/page.tsx: resolves token, dispatches to loadForShare + renderSharedView, friendly error for invalid/expired tokens, noindex - Middleware /s/* exemption confirmed present (no change needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
2.1 KiB
TypeScript
88 lines
2.1 KiB
TypeScript
import type { ReactNode } from "react";
|
|
import type { ZodType } from "zod";
|
|
|
|
export type ActivityLogEntry = {
|
|
id: string;
|
|
householdId: string;
|
|
entityType: string;
|
|
entityId: string;
|
|
actorId: string | null;
|
|
action: string;
|
|
payload: Record<string, unknown> | null;
|
|
createdAt: Date;
|
|
};
|
|
|
|
export type ShareCapabilities = {
|
|
canShare: boolean;
|
|
defaultCapabilities?: string[];
|
|
};
|
|
|
|
export type ReminderCapabilities = {
|
|
canRemind: boolean;
|
|
};
|
|
|
|
export type SearchResult = {
|
|
id: string;
|
|
title: string;
|
|
url: string;
|
|
excerpt?: string;
|
|
};
|
|
|
|
export type SearchAdapter = {
|
|
search: (query: string, householdId: string) => Promise<SearchResult[]>;
|
|
};
|
|
|
|
export type WidgetContext = {
|
|
userId: string;
|
|
householdId: string;
|
|
};
|
|
|
|
export type DashboardWidget = {
|
|
id: string;
|
|
title: string;
|
|
description: string;
|
|
category?: string;
|
|
defaultSize: { w: number; h: number };
|
|
minSize?: { w: number; h: number };
|
|
maxSize?: { w: number; h: number };
|
|
defaultPriority: number;
|
|
configSchema: ZodType;
|
|
defaultConfig: unknown;
|
|
resolveConfigOptions?: (ctx: WidgetContext) => Promise<unknown>;
|
|
render: (props: { config: unknown; ctx: WidgetContext }) => ReactNode;
|
|
};
|
|
|
|
export type QuickAddAction = {
|
|
id: string;
|
|
label: string;
|
|
icon?: string;
|
|
/** Navigation target used by the FAB sheet and command palette. */
|
|
url: string;
|
|
action?: (ctx: WidgetContext) => void | Promise<void>;
|
|
};
|
|
|
|
export type EntityTypeRegistration = {
|
|
type: string;
|
|
label: { singular: string; plural: string };
|
|
share?: ShareCapabilities;
|
|
reminder?: ReminderCapabilities;
|
|
search?: SearchAdapter;
|
|
resolveUrl: (id: string) => string;
|
|
loadForShare?: (id: string) => Promise<unknown>;
|
|
renderSharedView?: (props: {
|
|
data: unknown;
|
|
capabilities: { read: boolean; write: boolean };
|
|
token: string;
|
|
}) => ReactNode;
|
|
renderActivity?: (entry: ActivityLogEntry) => string;
|
|
};
|
|
|
|
export type ModuleManifest = {
|
|
id: string;
|
|
name: string;
|
|
nav?: { href: string; label: string; icon?: string };
|
|
entities: EntityTypeRegistration[];
|
|
dashboardWidgets?: DashboardWidget[];
|
|
quickAdds?: QuickAddAction[];
|
|
};
|