fix(work-record): complete monthly archive release
This commit is contained in:
parent
0b12ec28b9
commit
6e140e1445
@ -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,
|
||||
};
|
||||
|
||||
@ -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 };
|
||||
}
|
||||
|
||||
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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,
|
||||
}),
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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" },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user