Files
ftb-project-management/apps/web/stores/useOvertimeStore.ts
2026-07-03 09:43:36 +08:00

115 lines
4.2 KiB
TypeScript

'use client';
import { create } from 'zustand';
import type { OvertimeRecord } from '@/lib/overtime';
import type { DictItem } from '@/lib/requirement';
import { calcDuration } from '@/lib/overtime';
import { loadServerData, saveServerData, SERVER_DATA_CACHE_MS } from '@/lib/server-data';
const PRESET_REASONS: DictItem[] = [
{ id: 'reason-1', name: '需求变更', createdAt: '2024-01-01' },
{ id: 'reason-2', name: '需求新增', createdAt: '2024-01-01' },
{ id: 'reason-3', name: '版本冲刺', createdAt: '2024-01-01' },
{ id: 'reason-4', name: 'Bug修复', createdAt: '2024-01-01' },
{ id: 'reason-5', name: '线上问题修复', createdAt: '2024-01-01' },
{ id: 'reason-6', name: '测试问题整改', createdAt: '2024-01-01' },
{ id: 'reason-7', name: '技术难点攻关', createdAt: '2024-01-01' },
{ id: 'reason-8', name: '联调问题', createdAt: '2024-01-01' },
{ id: 'reason-9', name: '上游交付延误', createdAt: '2024-01-01' },
{ id: 'reason-10', name: '工作量超负荷', createdAt: '2024-01-01' },
{ id: 'reason-11', name: '返工修改', createdAt: '2024-01-01' },
];
const MOCK_RECORDS: OvertimeRecord[] = [];
let lastOvertimeFetchAt = 0;
interface OvertimeState {
records: OvertimeRecord[];
reasons: DictItem[];
loading: boolean;
loaded: boolean;
fetchRecords: (options?: { force?: boolean }) => Promise<void>;
createRecord: (data: Omit<OvertimeRecord, 'id' | 'duration' | 'createdAt'>) => void;
updateRecord: (id: string, data: Partial<OvertimeRecord>) => void;
deleteRecord: (id: string) => void;
addReason: (name: string) => void;
updateReason: (id: string, name: string) => void;
deleteReason: (id: string) => void;
}
function saveStored(data: { records: OvertimeRecord[]; reasons: DictItem[] }) {
saveServerData('overtime', data).catch(() => {});
}
async function loadStored(): Promise<{ records: OvertimeRecord[]; reasons: DictItem[] } | null> {
try {
return await loadServerData<{ records: OvertimeRecord[]; reasons: DictItem[] }>('overtime');
} catch {}
return null;
}
export const useOvertimeStore = create<OvertimeState>((set, get) => ({
records: MOCK_RECORDS,
reasons: PRESET_REASONS,
loading: false,
loaded: false,
fetchRecords: async (options) => {
if (!options?.force && get().loaded && Date.now() - lastOvertimeFetchAt < SERVER_DATA_CACHE_MS) return;
const cached = await loadStored();
if (!options?.force && get().loaded && Date.now() - lastOvertimeFetchAt < SERVER_DATA_CACHE_MS) return;
lastOvertimeFetchAt = Date.now();
set({ records: cached?.records ?? MOCK_RECORDS, reasons: cached?.reasons ?? PRESET_REASONS, loaded: true });
},
createRecord: (data) => {
const duration = calcDuration(data.startTime, data.endTime);
const record: OvertimeRecord = {
...data,
id: `ot-${Date.now()}`,
duration,
createdAt: new Date().toISOString().slice(0, 10),
};
const records = [...get().records, record];
set({ records, loaded: true });
saveStored({ records, reasons: get().reasons });
},
updateRecord: (id, data) => {
const records = get().records.map((r) => {
if (r.id !== id) return r;
const updated = { ...r, ...data };
if (data.startTime || data.endTime) {
updated.duration = calcDuration(updated.startTime, updated.endTime);
}
return updated;
});
set({ records, loaded: true });
saveStored({ records, reasons: get().reasons });
},
deleteRecord: (id) => {
const records = get().records.filter((r) => r.id !== id);
set({ records, loaded: true });
saveStored({ records, reasons: get().reasons });
},
addReason: (name) => {
const item: DictItem = { id: `reason-${Date.now()}`, name, createdAt: new Date().toISOString().slice(0, 10) };
const reasons = [...get().reasons, item];
set({ reasons, loaded: true });
saveStored({ records: get().records, reasons });
},
updateReason: (id, name) => {
const reasons = get().reasons.map((r) => r.id === id ? { ...r, name } : r);
set({ reasons, loaded: true });
saveStored({ records: get().records, reasons });
},
deleteReason: (id) => {
const reasons = get().reasons.filter((r) => r.id !== id);
set({ reasons, loaded: true });
saveStored({ records: get().records, reasons });
},
}));