multica/apps/web/features/inbox/store.ts
Naiyuan Qing 2cf088ddf6 feat: resizable sidebar, issue detail rewrite, package consolidation
- Add drag-to-resize sidebar with localStorage persistence
- Rewrite issue detail page with Tiptap rich text editor, due date picker, acceptance criteria
- Redesign create-issue modal with pill-based property toolbar and expand/collapse
- Consolidate @multica/sdk and @multica/types into apps/web/shared/
- Simplify auth: remove verification codes, PATs, email service (dev-only login)
- Add 401 unauthorized handler to redirect expired sessions to login
- Fix due date format to send full RFC3339 timestamps
- Increase description editor debounce to 1500ms
- Remove arbitrary Tailwind values in create-issue modal
- Renumber migrations (inbox_actor 012→009), remove unused migrations
- UI polish across agents, settings, inbox, knowledge-base pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:47:04 +08:00

78 lines
2.1 KiB
TypeScript

"use client";
import { create } from "zustand";
import type { InboxItem, IssueStatus } from "@/shared/types";
import { api } from "@/shared/api";
import { createLogger } from "@/shared/logger";
const logger = createLogger("inbox-store");
interface InboxState {
items: InboxItem[];
loading: boolean;
fetch: () => Promise<void>;
setItems: (items: InboxItem[]) => void;
addItem: (item: InboxItem) => void;
markRead: (id: string) => void;
archive: (id: string) => void;
markAllRead: () => void;
archiveAll: () => void;
archiveAllRead: () => void;
updateIssueStatus: (issueId: string, status: IssueStatus) => void;
unreadCount: () => number;
}
export const useInboxStore = create<InboxState>((set, get) => ({
items: [],
loading: true,
fetch: async () => {
logger.debug("fetch start");
set({ loading: true });
try {
const data = await api.listInbox();
logger.info("fetched", data.length, "items");
set({ items: data, loading: false });
} catch (err) {
logger.error("fetch failed", err);
set({ loading: false });
}
},
setItems: (items) => set({ items }),
addItem: (item) =>
set((s) => ({
items: s.items.some((i) => i.id === item.id)
? s.items
: [item, ...s.items],
})),
markRead: (id) =>
set((s) => ({
items: s.items.map((i) => (i.id === id ? { ...i, read: true } : i)),
})),
archive: (id) =>
set((s) => ({
items: s.items.map((i) => (i.id === id ? { ...i, archived: true } : i)),
})),
markAllRead: () =>
set((s) => ({
items: s.items.map((i) => (!i.archived ? { ...i, read: true } : i)),
})),
archiveAll: () =>
set((s) => ({
items: s.items.map((i) => (!i.archived ? { ...i, archived: true } : i)),
})),
archiveAllRead: () =>
set((s) => ({
items: s.items.map((i) =>
i.read && !i.archived ? { ...i, archived: true } : i
),
})),
updateIssueStatus: (issueId, status) =>
set((s) => ({
items: s.items.map((i) =>
i.issue_id === issueId ? { ...i, issue_status: status } : i
),
})),
unreadCount: () => get().items.filter((i) => !i.read && !i.archived).length,
}));