关键改动: - 支持需求覆盖和调研方向开始工作记录 - 日报按计划记录开始时间和进度下次开始时间计算证据 - 更新版本编辑校验、项目展示和 workflow 说明 Co-Authored-By: Codex GPT-5 <codex@openai.com>
78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
'use client';
|
|
import { create } from 'zustand';
|
|
import { formatLocalDate } from '@/lib/format';
|
|
import { mergeWorkActivities, type WorkActivity, type WorkActivityDraft } from '@/lib/work-activity';
|
|
import { makeProgressNoteActivity, type ProgressNoteActivityInput } from '@/lib/work-activity-factory';
|
|
import { loadServerData, saveServerData } from '@/lib/server-data';
|
|
|
|
interface WorkActivityState {
|
|
activities: WorkActivity[];
|
|
fetchActivities: () => Promise<void>;
|
|
addActivity: (data: WorkActivityDraft) => WorkActivity;
|
|
addProgressNote: (data: ProgressNoteActivityInput) => WorkActivity;
|
|
deleteActivity: (id: string) => void;
|
|
}
|
|
|
|
async function saveStored(
|
|
items: WorkActivity[],
|
|
setActivities?: (items: WorkActivity[]) => void,
|
|
options: { mergeRemote?: boolean } = {},
|
|
) {
|
|
try {
|
|
if (options.mergeRemote === false) {
|
|
await saveServerData('work-activities', items);
|
|
return;
|
|
}
|
|
const remote = await loadServerData<WorkActivity[]>('work-activities');
|
|
const merged = mergeWorkActivities(Array.isArray(remote) ? remote : [], items);
|
|
setActivities?.(merged);
|
|
await saveServerData('work-activities', merged);
|
|
} catch {
|
|
saveServerData('work-activities', items).catch(() => {});
|
|
}
|
|
}
|
|
|
|
async function loadStored(): Promise<WorkActivity[] | null> {
|
|
try {
|
|
return await loadServerData<WorkActivity[]>('work-activities');
|
|
} catch {}
|
|
return null;
|
|
}
|
|
|
|
function createActivityId(): string {
|
|
return `act-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
}
|
|
|
|
export const useWorkActivityStore = create<WorkActivityState>((set, get) => ({
|
|
activities: [],
|
|
|
|
fetchActivities: async () => {
|
|
const cached = await loadStored();
|
|
if (cached) set({ activities: cached });
|
|
},
|
|
|
|
addActivity: (data) => {
|
|
const now = new Date();
|
|
const item: WorkActivity = {
|
|
...data,
|
|
id: createActivityId(),
|
|
date: data.date ?? formatLocalDate(now),
|
|
occurredAt: data.occurredAt ?? now.toISOString(),
|
|
};
|
|
const updated = [...get().activities, item];
|
|
set({ activities: updated });
|
|
saveStored(updated, (activities) => set({ activities }));
|
|
return item;
|
|
},
|
|
|
|
addProgressNote: (data) => {
|
|
return get().addActivity(makeProgressNoteActivity(data));
|
|
},
|
|
|
|
deleteActivity: (id) => {
|
|
const updated = get().activities.filter((activity) => activity.id !== id);
|
|
set({ activities: updated });
|
|
saveStored(updated, (activities) => set({ activities }), { mergeRemote: false });
|
|
},
|
|
}));
|