fix(work-record): complete monthly archive release

This commit is contained in:
WuYongJie 2026-09-01 12:24:38 +08:00
parent 0b12ec28b9
commit 6e140e1445
10 changed files with 92 additions and 1 deletions

View File

@ -74,6 +74,7 @@ export function buildIntakeWorkspacePanelProps(params: {
) => void;
onCloseNewProductWorkspace: (tabId: string) => void;
onMaterialLibraryFinishesChange: (finishes: MaterialDTO[]) => void;
onOpenWorkRecordArchiveDetailWorkspace: (monthKey: string, label?: string) => void;
onActivate: (tabId: string) => void;
onClose: (tabId: string) => void;
}): Omit<IntakeWorkspaceContentPanelProps, "mainContent"> {
@ -118,6 +119,8 @@ export function buildIntakeWorkspacePanelProps(params: {
params.onActivatePendingEntryListWorkspace,
onCloseNewProductWorkspace: params.onCloseNewProductWorkspace,
onMaterialLibraryFinishesChange: params.onMaterialLibraryFinishesChange,
onOpenWorkRecordArchiveDetailWorkspace:
params.onOpenWorkRecordArchiveDetailWorkspace,
onActivate: params.onActivate,
onClose: params.onClose,
};

View File

@ -149,6 +149,9 @@ export function serializeIntakeWorkspaceTab(
) {
return { id: tab.id, kind: tab.kind };
}
if (tab.kind === "workRecordArchiveDetail") {
return { id: tab.id, kind: tab.kind, monthKey: tab.monthKey };
}
return { id: tab.id, kind: tab.kind };
}

View File

@ -25,6 +25,7 @@ export function getWorkspaceTabPageKey(tab: IntakeWorkspaceTab) {
if (tab.kind === "workRecord") return "work-record:list";
if (tab.kind === "workRecordScores") return "work-record:scores";
if (tab.kind === "workRecordArchives") return "work-record:archives";
if (tab.kind === "workRecordArchiveDetail") return `work-record:archive:${tab.monthKey}`;
if (tab.kind === "productPatentLedger") {
const target = tab.target;
return [
@ -54,6 +55,7 @@ function isSingletonWorkspace(tab: IntakeWorkspaceTab) {
tab.kind === "workRecord" ||
tab.kind === "workRecordScores" ||
tab.kind === "workRecordArchives"
|| tab.kind === "workRecordArchiveDetail"
);
}

View File

@ -22,6 +22,7 @@ export type IntakeSection =
| "workRecord"
| "workRecordScores"
| "workRecordArchives"
| "workRecordArchiveDetail"
| null;
export type IntakeEntryTab = "integrated" | "nonIntegrated" | "stock";
@ -93,6 +94,12 @@ export type IntakeWorkspaceTab =
kind: "workRecordArchives";
label: string;
}
| {
id: string;
kind: "workRecordArchiveDetail";
label: string;
monthKey: string;
}
| {
id: string;
kind: "nonIntegratedProduct";
@ -174,6 +181,11 @@ export type PersistedIntakeWorkspaceTab =
id: string;
kind: "workRecordArchives";
}
| {
id: string;
kind: "workRecordArchiveDetail";
monthKey: string;
}
| {
id: string;
kind: "nonIntegratedProduct";

View File

@ -186,6 +186,7 @@ export function useIntakeClientViewModel(
openWorkRecordWorkspace,
openWorkRecordScoreWorkspace,
openWorkRecordArchiveWorkspace,
openWorkRecordArchiveDetailWorkspace,
replaceCreateWorkspaceWithProduct,
} = useIntakeWorkspaceOpeners({
activeWorkspaceTabId,
@ -298,6 +299,7 @@ export function useIntakeClientViewModel(
onActivatePendingEntryListWorkspace: activatePendingEntryListWorkspace,
onCloseNewProductWorkspace: closeNewNonIntegratedProductWorkspace,
onMaterialLibraryFinishesChange: setMaterialLibraryFinishes,
onOpenWorkRecordArchiveDetailWorkspace: openWorkRecordArchiveDetailWorkspace,
onActivate: activateWorkspaceTab,
onClose: closeWorkspaceTab,
}),

View File

@ -26,6 +26,7 @@ import {
createReferenceEstimatorWorkspaceTab,
createSpuRuleLibraryWorkspaceTab,
createWorkRecordArchiveWorkspaceTab,
createWorkRecordArchiveDetailWorkspaceTab,
createWorkRecordWorkspaceTab,
createWorkRecordScoreWorkspaceTab,
} from "@/app/intake/intakeWorkspaceState";
@ -510,6 +511,17 @@ export function useIntakeWorkspaceOpeners({
);
}, [canViewWorkRecordArchives, createWorkspaceTabId, openWorkspaceTab]);
const openWorkRecordArchiveDetailWorkspace = useCallback((monthKey: string, label?: string) => {
if (!canViewWorkRecordArchives) return;
openWorkspaceTab(
createWorkRecordArchiveDetailWorkspaceTab({
id: createWorkspaceTabId("work-record-archive", monthKey),
monthKey,
label,
}),
);
}, [canViewWorkRecordArchives, createWorkspaceTabId, openWorkspaceTab]);
return {
activatePendingEntryListWorkspace,
closeNewNonIntegratedProductWorkspace,
@ -530,6 +542,7 @@ export function useIntakeWorkspaceOpeners({
openWorkRecordWorkspace,
openWorkRecordScoreWorkspace,
openWorkRecordArchiveWorkspace,
openWorkRecordArchiveDetailWorkspace,
replaceCreateWorkspaceWithProduct,
};
}

View File

@ -130,6 +130,7 @@ function WorkRecordColumnResizeFrame({
export function WorkRecordTable({
canEditRecords,
canEditAllRecords = false,
currentUserId,
onCommitRecord,
onCopyRecord,
@ -139,6 +140,7 @@ export function WorkRecordTable({
scoreRules,
}: {
canEditRecords: boolean;
canEditAllRecords?: boolean;
currentUserId: string;
onCommitRecord: (id: string) => void;
onCopyRecord: (id: string) => void;
@ -203,7 +205,7 @@ export function WorkRecordTable({
</thead>
<tbody>
{records.map((record) => {
const canEdit = canEditRecords && record.designerId === currentUserId;
const canEdit = canEditRecords && (canEditAllRecords || record.designerId === currentUserId);
const secondaryOptions = scoreRules.filter((rule) => rule.primary === record.primary);
const requiredErrors = getWorkRecordRequiredFieldErrors(record);
const isOther = isOtherWorkRecord(record.primary);

View File

@ -2,6 +2,7 @@ import type {
WorkRecordFilters,
WorkRecordListResponse,
WorkRecordMonthlyArchive,
WorkRecordMonthlyArchiveDetail,
WorkRecordRow,
WorkRecordScoreRule,
WorkRecordScoreSummary,
@ -132,3 +133,36 @@ export async function fetchWorkRecordMonthlyArchives() {
const response = await fetch("/api/intake/work-record/monthly-archives", { cache: "no-store" });
return readApiData<{ archives: WorkRecordMonthlyArchive[] }>(response);
}
export async function fetchWorkRecordMonthlyArchive(monthKey: string) {
const response = await fetch(`/api/intake/work-record/monthly-archives/${encodeURIComponent(monthKey)}`, { cache: "no-store" });
return readApiData<{ detail: WorkRecordMonthlyArchiveDetail }>(response);
}
function archiveRecordUrl(monthKey: string, id?: string) {
const base = `/api/intake/work-record/monthly-archives/${encodeURIComponent(monthKey)}/records`;
return id ? `${base}/${encodeURIComponent(id)}` : base;
}
export async function createWorkRecordMonthlyArchiveRecord(monthKey: string, record: Partial<WorkRecordRow>) {
const response = await fetch(archiveRecordUrl(monthKey), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ record }),
});
return readApiData<{ record: WorkRecordRow }>(response);
}
export async function updateWorkRecordMonthlyArchiveRecord(monthKey: string, id: string, record: Partial<WorkRecordRow>) {
const response = await fetch(archiveRecordUrl(monthKey, id), {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ record }),
});
return readApiData<{ record: WorkRecordRow }>(response);
}
export async function deleteWorkRecordMonthlyArchiveRecord(monthKey: string, id: string) {
const response = await fetch(archiveRecordUrl(monthKey, id), { method: "DELETE" });
return readApiData<{ id: string }>(response);
}

View File

@ -46,6 +46,14 @@ export type WorkRecordMonthlyArchive = {
generatedAt: string;
};
export type WorkRecordMonthlyArchiveDetail = {
archive: WorkRecordMonthlyArchive;
records: WorkRecordRow[];
scoreRules: WorkRecordScoreRule[];
designers: string[];
summary: WorkRecordScoreSummary[];
};
export type WorkRecordListResponse = {
records: WorkRecordRow[];
scoreRules: WorkRecordScoreRule[];
@ -55,3 +63,5 @@ export type WorkRecordListResponse = {
end: string;
};
};
export type WorkRecordArchiveResponse = WorkRecordMonthlyArchiveDetail;

View File

@ -177,6 +177,16 @@ class WorkRecordRepository {
return prisma.workRecordMonthlyArchive.create({ data });
}
async updateArchive(
monthKey: string,
data: Prisma.WorkRecordMonthlyArchiveUpdateInput,
) {
return prisma.workRecordMonthlyArchive.update({
where: { monthKey },
data,
});
}
async listArchives(): Promise<WorkRecordMonthlyArchive[]> {
return prisma.workRecordMonthlyArchive.findMany({
orderBy: { periodStart: "desc" },