fix: restore TQ consumer migrations lost during merge with main
The merge with origin/main (fe9479d6) silently reverted all consumer-side migrations, leaving core/ as dead code. Restored all 39 files from pre-merge commit6032b5df, plus main's trigger.config null fix for agents page. Verified: 59 @core/ imports across features/ and app/, all stores gutted/deleted, realtime sync uses queryClient not Zustand. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7ed565da6b
commit
6296629831
4 changed files with 6 additions and 208 deletions
|
|
@ -44,9 +44,8 @@ import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip
|
|||
import { useAuthStore } from "@/features/auth";
|
||||
import { useWorkspaceStore } from "@/features/workspace";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { inboxKeys } from "@core/inbox/queries";
|
||||
import { deduplicateInboxItems } from "@core/inbox/queries";
|
||||
import { api } from "@/shared/api";
|
||||
import { useWorkspaceId } from "@core/hooks";
|
||||
import { inboxListOptions, deduplicateInboxItems } from "@core/inbox/queries";
|
||||
import { useModalStore } from "@/features/modals";
|
||||
|
||||
const primaryNav = [
|
||||
|
|
@ -77,12 +76,8 @@ export function AppSidebar() {
|
|||
const workspaces = useWorkspaceStore((s) => s.workspaces);
|
||||
const switchWorkspace = useWorkspaceStore((s) => s.switchWorkspace);
|
||||
|
||||
const wsId = workspace?.id;
|
||||
const { data: inboxItems = [] } = useQuery({
|
||||
queryKey: wsId ? inboxKeys.list(wsId) : ["inbox", "disabled"],
|
||||
queryFn: () => api.listInbox(),
|
||||
enabled: !!wsId,
|
||||
});
|
||||
const wsId = useWorkspaceId();
|
||||
const { data: inboxItems = [] } = useQuery(inboxListOptions(wsId));
|
||||
const unreadCount = React.useMemo(
|
||||
() => deduplicateInboxItems(inboxItems).filter((i) => !i.read).length,
|
||||
[inboxItems],
|
||||
|
|
|
|||
|
|
@ -995,7 +995,7 @@ function TriggersTab({
|
|||
value={scheduledConfig.cron ?? ""}
|
||||
onChange={(e) =>
|
||||
updateTriggerConfig(trigger.id, {
|
||||
...scheduledConfig,
|
||||
...(trigger.config ?? {}),
|
||||
cron: e.target.value,
|
||||
})
|
||||
}
|
||||
|
|
@ -1012,7 +1012,7 @@ function TriggersTab({
|
|||
value={scheduledConfig.timezone ?? ""}
|
||||
onChange={(e) =>
|
||||
updateTriggerConfig(trigger.id, {
|
||||
...scheduledConfig,
|
||||
...(trigger.config ?? {}),
|
||||
timezone: e.target.value,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import { create } from "zustand";
|
||||
import type { InboxItem, IssueStatus } from "@/shared/types";
|
||||
import { toast } from "sonner";
|
||||
import { api } from "@/shared/api";
|
||||
import { createLogger } from "@/shared/logger";
|
||||
|
||||
const logger = createLogger("inbox-store");
|
||||
|
||||
/**
|
||||
* Deduplicate inbox items by issue_id (one entry per issue, Linear-style),
|
||||
* keep latest, sort by time DESC.
|
||||
* Memoized by reference — returns the same array if `items` hasn't changed.
|
||||
*/
|
||||
let _prevItems: InboxItem[] = [];
|
||||
let _prevDeduped: InboxItem[] = [];
|
||||
|
||||
function deduplicateInboxItems(items: InboxItem[]): InboxItem[] {
|
||||
if (items === _prevItems) return _prevDeduped;
|
||||
_prevItems = items;
|
||||
|
||||
const active = items.filter((i) => !i.archived);
|
||||
const groups = new Map<string, InboxItem[]>();
|
||||
active.forEach((item) => {
|
||||
const key = item.issue_id ?? item.id;
|
||||
const group = groups.get(key) ?? [];
|
||||
group.push(item);
|
||||
groups.set(key, group);
|
||||
});
|
||||
const merged: InboxItem[] = [];
|
||||
groups.forEach((group) => {
|
||||
const sorted = group.sort(
|
||||
(a, b) =>
|
||||
new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),
|
||||
);
|
||||
if (sorted[0]) merged.push(sorted[0]);
|
||||
});
|
||||
_prevDeduped = merged.sort(
|
||||
(a, b) =>
|
||||
new Date(b.created_at).getTime() - new Date(a.created_at).getTime(),
|
||||
);
|
||||
return _prevDeduped;
|
||||
}
|
||||
|
||||
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;
|
||||
dedupedItems: () => InboxItem[];
|
||||
unreadCount: () => number;
|
||||
}
|
||||
|
||||
export const useInboxStore = create<InboxState>((set, get) => ({
|
||||
items: [],
|
||||
loading: true,
|
||||
|
||||
fetch: async () => {
|
||||
logger.debug("fetch start");
|
||||
const isInitialLoad = get().items.length === 0;
|
||||
if (isInitialLoad) 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);
|
||||
toast.error("Failed to load inbox");
|
||||
if (isInitialLoad) 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) => {
|
||||
const target = s.items.find((i) => i.id === id);
|
||||
const issueId = target?.issue_id;
|
||||
return {
|
||||
items: s.items.map((i) =>
|
||||
i.id === id || (issueId && i.issue_id === issueId)
|
||||
? { ...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
|
||||
),
|
||||
})),
|
||||
dedupedItems: () => deduplicateInboxItems(get().items),
|
||||
unreadCount: () =>
|
||||
get().dedupedItems().filter((i) => !i.read).length,
|
||||
}));
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import { create } from "zustand";
|
||||
import type { AgentRuntime } from "@/shared/types";
|
||||
import { api } from "@/shared/api";
|
||||
import { useWorkspaceStore } from "@/features/workspace";
|
||||
|
||||
interface RuntimeState {
|
||||
runtimes: AgentRuntime[];
|
||||
selectedId: string;
|
||||
fetching: boolean;
|
||||
}
|
||||
|
||||
interface RuntimeActions {
|
||||
fetchRuntimes: () => Promise<void>;
|
||||
setSelectedId: (id: string) => void;
|
||||
/** Patch a single runtime in-place (e.g. status/last_seen_at from WS event). */
|
||||
patchRuntime: (id: string, updates: Partial<AgentRuntime>) => void;
|
||||
/** Replace the full runtimes list (used on daemon:register events). */
|
||||
setRuntimes: (runtimes: AgentRuntime[]) => void;
|
||||
}
|
||||
|
||||
type RuntimeStore = RuntimeState & RuntimeActions;
|
||||
|
||||
export const useRuntimeStore = create<RuntimeStore>((set, get) => ({
|
||||
// State
|
||||
runtimes: [],
|
||||
selectedId: "",
|
||||
fetching: true,
|
||||
|
||||
// Actions
|
||||
fetchRuntimes: async () => {
|
||||
const workspace = useWorkspaceStore.getState().workspace;
|
||||
if (!workspace) return;
|
||||
try {
|
||||
const data = await api.listRuntimes({ workspace_id: workspace.id });
|
||||
const { selectedId } = get();
|
||||
set({
|
||||
runtimes: data,
|
||||
fetching: false,
|
||||
// Auto-select first if nothing selected
|
||||
selectedId: selectedId && data.some((r) => r.id === selectedId)
|
||||
? selectedId
|
||||
: data[0]?.id ?? "",
|
||||
});
|
||||
} catch {
|
||||
set({ fetching: false });
|
||||
}
|
||||
},
|
||||
|
||||
setSelectedId: (id) => set({ selectedId: id }),
|
||||
|
||||
patchRuntime: (id, updates) => {
|
||||
set((state) => ({
|
||||
runtimes: state.runtimes.map((r) =>
|
||||
r.id === id ? { ...r, ...updates } : r,
|
||||
),
|
||||
}));
|
||||
},
|
||||
|
||||
setRuntimes: (runtimes) => {
|
||||
const { selectedId } = get();
|
||||
set({
|
||||
runtimes,
|
||||
selectedId: selectedId && runtimes.some((r) => r.id === selectedId)
|
||||
? selectedId
|
||||
: runtimes[0]?.id ?? "",
|
||||
});
|
||||
},
|
||||
}));
|
||||
Loading…
Add table
Add a link
Reference in a new issue