Some checks failed
Deploy Production / Build, push, deploy, verify (push) Has been cancelled
- 移除已迁移业务 AppData 运行时 fallback,改走领域 API 和关系表快读 - 补齐需求产品负责人、版本计划任务 JSON 和成员 username 回填迁移 - 统一治理字典入口,并补充 AI provider、数据源契约和领域服务测试 Co-Authored-By: Codex GPT-5 <codex@openai.com>
85 lines
2.9 KiB
TypeScript
85 lines
2.9 KiB
TypeScript
'use client';
|
|
import { create } from 'zustand';
|
|
import { formatLocalDate } from '@/lib/format';
|
|
import { type WorkActivity, type WorkActivityDraft } from '@/lib/work-activity';
|
|
import { makeProgressNoteActivity, type ProgressNoteActivityInput } from '@/lib/work-activity-factory';
|
|
import {
|
|
createWorkActivityDomain,
|
|
deleteWorkActivityDomain,
|
|
listWorkActivitiesDomain,
|
|
} from '@/lib/domain-api';
|
|
import { scheduleSaveWithOptimisticRollback } from '@/lib/optimistic-persistence';
|
|
import { SERVER_DATA_CACHE_MS } from '@/lib/server-data';
|
|
|
|
interface WorkActivityState {
|
|
activities: WorkActivity[];
|
|
loaded: boolean;
|
|
fetchActivities: (options?: { force?: boolean }) => Promise<void>;
|
|
addActivity: (data: WorkActivityDraft) => WorkActivity;
|
|
addProgressNote: (data: ProgressNoteActivityInput) => WorkActivity;
|
|
deleteActivity: (id: string) => void;
|
|
}
|
|
|
|
let lastActivitiesFetchAt = 0;
|
|
|
|
function createActivityId(): string {
|
|
return `act-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
}
|
|
|
|
export const useWorkActivityStore = create<WorkActivityState>((set, get) => ({
|
|
activities: [],
|
|
loaded: false,
|
|
|
|
fetchActivities: async (options) => {
|
|
if (!options?.force && get().loaded && Date.now() - lastActivitiesFetchAt < SERVER_DATA_CACHE_MS) return;
|
|
const cached = await listWorkActivitiesDomain().catch(() => []);
|
|
if (!options?.force && get().loaded && Date.now() - lastActivitiesFetchAt < SERVER_DATA_CACHE_MS) return;
|
|
lastActivitiesFetchAt = Date.now();
|
|
set({ activities: cached ?? [], loaded: true });
|
|
},
|
|
|
|
addActivity: (data) => {
|
|
const previous = get().activities;
|
|
const now = new Date();
|
|
const item: WorkActivity = {
|
|
...data,
|
|
id: createActivityId(),
|
|
date: data.date ?? formatLocalDate(now),
|
|
occurredAt: data.occurredAt ?? now.toISOString(),
|
|
};
|
|
const updated = [...previous, item];
|
|
set({ activities: updated, loaded: true });
|
|
scheduleSaveWithOptimisticRollback({
|
|
save: async () => {
|
|
const created = await createWorkActivityDomain(item);
|
|
set({
|
|
activities: get().activities.map((activity) => (activity.id === item.id ? { ...item, ...created } : activity)),
|
|
loaded: true,
|
|
});
|
|
},
|
|
expected: updated,
|
|
getCurrent: () => get().activities,
|
|
rollback: () => set({ activities: previous, loaded: true }),
|
|
});
|
|
return item;
|
|
},
|
|
|
|
addProgressNote: (data) => {
|
|
return get().addActivity(makeProgressNoteActivity(data));
|
|
},
|
|
|
|
deleteActivity: (id) => {
|
|
const previous = get().activities;
|
|
const updated = previous.filter((activity) => activity.id !== id);
|
|
set({ activities: updated, loaded: true });
|
|
scheduleSaveWithOptimisticRollback({
|
|
save: async () => {
|
|
await deleteWorkActivityDomain(id);
|
|
},
|
|
expected: updated,
|
|
getCurrent: () => get().activities,
|
|
rollback: () => set({ activities: previous, loaded: true }),
|
|
});
|
|
},
|
|
}));
|