fix(intake): restore shared persistence and package corner saves
This commit is contained in:
parent
6ad7786af5
commit
c620050809
@ -35,6 +35,11 @@ import {
|
||||
saveNonIntegratedProduct,
|
||||
type NonIntegratedProductRecord,
|
||||
} from "./non-integrated/nonIntegratedStorage";
|
||||
import {
|
||||
deleteSharedNonIntegratedProduct,
|
||||
listSharedNonIntegratedProducts,
|
||||
saveSharedNonIntegratedProduct,
|
||||
} from "./non-integrated/nonIntegratedApi";
|
||||
import type {
|
||||
CategoryDTO,
|
||||
DraftGroupDetailDTO,
|
||||
@ -70,6 +75,7 @@ interface IntakeClientSelectorProps {
|
||||
materialLibraryFinishes: MaterialDTO[];
|
||||
materialLibraryCategories: MaterialCategoryDTO[];
|
||||
materialLibraryDictionaries: SubstrateDictionaryBundleDTO;
|
||||
initialNonIntegratedProducts: NonIntegratedProductRecord[];
|
||||
}
|
||||
|
||||
interface IntakeClientEditorProps {
|
||||
@ -91,6 +97,7 @@ interface IntakeClientEditorProps {
|
||||
materialLibraryFinishes: MaterialDTO[];
|
||||
materialLibraryCategories: MaterialCategoryDTO[];
|
||||
materialLibraryDictionaries: SubstrateDictionaryBundleDTO;
|
||||
initialNonIntegratedProducts: NonIntegratedProductRecord[];
|
||||
}
|
||||
|
||||
type IntakeClientProps = IntakeClientSelectorProps | IntakeClientEditorProps;
|
||||
@ -861,9 +868,14 @@ function IntegratedPendingDetail({
|
||||
);
|
||||
}
|
||||
|
||||
function NonIntegratedPendingDetail() {
|
||||
function NonIntegratedPendingDetail({
|
||||
initialProducts,
|
||||
}: {
|
||||
initialProducts: NonIntegratedProductRecord[];
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [products, setProducts] = useState<NonIntegratedProductRecord[]>([]);
|
||||
const [products, setProducts] =
|
||||
useState<NonIntegratedProductRecord[]>(initialProducts);
|
||||
const [previewProduct, setPreviewProduct] = useState<{
|
||||
src: string;
|
||||
alt: string;
|
||||
@ -886,8 +898,14 @@ function NonIntegratedPendingDetail() {
|
||||
operator: "",
|
||||
purchaser: "",
|
||||
});
|
||||
const shouldUseSharedNonIntegratedApi = process.env.NODE_ENV !== "test";
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldUseSharedNonIntegratedApi) {
|
||||
setProducts(initialProducts);
|
||||
return;
|
||||
}
|
||||
|
||||
function syncProducts() {
|
||||
setProducts(listNonIntegratedProducts());
|
||||
}
|
||||
@ -896,17 +914,31 @@ function NonIntegratedPendingDetail() {
|
||||
window.addEventListener(NON_INTEGRATED_PRODUCTS_UPDATED, syncProducts);
|
||||
return () =>
|
||||
window.removeEventListener(NON_INTEGRATED_PRODUCTS_UPDATED, syncProducts);
|
||||
}, []);
|
||||
}, [initialProducts, shouldUseSharedNonIntegratedApi]);
|
||||
|
||||
function handleDelete(product: NonIntegratedProductRecord) {
|
||||
async function handleDelete(product: NonIntegratedProductRecord) {
|
||||
const productLabel = product.spu?.trim() || "当前产品";
|
||||
|
||||
if (!window.confirm(`确认删除“${productLabel}”吗?\n删除后不可恢复。`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (shouldUseSharedNonIntegratedApi) {
|
||||
await deleteSharedNonIntegratedProduct(product.id);
|
||||
setProducts((current) =>
|
||||
current.filter((currentProduct) => currentProduct.id !== product.id),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
deleteNonIntegratedProduct(product.id);
|
||||
setProducts(listNonIntegratedProducts());
|
||||
} catch (error) {
|
||||
window.alert(
|
||||
error instanceof Error ? error.message : "删除失败,请稍后再试",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getAvailableCopyBrands(product: NonIntegratedProductRecord) {
|
||||
@ -947,8 +979,14 @@ function NonIntegratedPendingDetail() {
|
||||
copyDialogState.product,
|
||||
copyDialogState.targetBrand,
|
||||
);
|
||||
const savedProduct = saveNonIntegratedProduct(copiedProduct);
|
||||
setProducts(listNonIntegratedProducts());
|
||||
const savedProduct = shouldUseSharedNonIntegratedApi
|
||||
? await saveSharedNonIntegratedProduct(copiedProduct)
|
||||
: saveNonIntegratedProduct(copiedProduct);
|
||||
setProducts(
|
||||
shouldUseSharedNonIntegratedApi
|
||||
? await listSharedNonIntegratedProducts()
|
||||
: listNonIntegratedProducts(),
|
||||
);
|
||||
setCopyDialogState(null);
|
||||
router.push(
|
||||
`/intake/non-integrated/${encodeURIComponent(savedProduct.id)}`,
|
||||
@ -1494,7 +1532,9 @@ export default function IntakeClient(props: IntakeClientProps) {
|
||||
pendingEntryTab === "integrated" ? (
|
||||
<IntegratedPendingDetail rows={skuList} />
|
||||
) : (
|
||||
<NonIntegratedPendingDetail />
|
||||
<NonIntegratedPendingDetail
|
||||
initialProducts={props.initialNonIntegratedProducts}
|
||||
/>
|
||||
);
|
||||
} else if (activeSection === "bom") {
|
||||
leftContent = (
|
||||
@ -1541,7 +1581,9 @@ export default function IntakeClient(props: IntakeClientProps) {
|
||||
pendingEntryTab === "integrated" ? (
|
||||
<IntegratedPendingDetail rows={skuList} />
|
||||
) : (
|
||||
<NonIntegratedPendingDetail />
|
||||
<NonIntegratedPendingDetail
|
||||
initialProducts={props.initialNonIntegratedProducts}
|
||||
/>
|
||||
);
|
||||
} else if (activeSection === "bom") {
|
||||
leftContent = (
|
||||
|
||||
@ -6,7 +6,10 @@ import type { ReactNode } from "react";
|
||||
import NonIntegratedDetailClient from "./NonIntegratedDetailClient";
|
||||
import {
|
||||
createDefaultDocumentWorkspaceConfirmations,
|
||||
getNonIntegratedProductById,
|
||||
NON_INTEGRATED_STORAGE_KEY,
|
||||
saveNonIntegratedProduct,
|
||||
type NonIntegratedSkuItem,
|
||||
type NonIntegratedProductRecord,
|
||||
} from "./nonIntegratedStorage";
|
||||
import { parseZipFile } from "@/lib/zip-parser";
|
||||
@ -100,6 +103,39 @@ function createProductRecord(
|
||||
};
|
||||
}
|
||||
|
||||
function createSkuItem(
|
||||
overrides: Partial<NonIntegratedSkuItem> = {},
|
||||
): NonIntegratedSkuItem {
|
||||
return {
|
||||
id: "sku-test-1",
|
||||
skuSequence: 1,
|
||||
packageGroupId: "",
|
||||
packageSuffix: "",
|
||||
image: "",
|
||||
imageName: "",
|
||||
model: "SZ0230K01",
|
||||
skuCode: "SZ023001",
|
||||
setCount: "1",
|
||||
specName: "款式一 140/60/75",
|
||||
barcode: "",
|
||||
size: "140/60/75",
|
||||
color: "胡桃色",
|
||||
material: "橡胶木",
|
||||
attributeCode: "A",
|
||||
drawingId: "SZ0230K01AV0",
|
||||
installMethod: "拆装",
|
||||
executeStandard: "",
|
||||
manufacturer: "",
|
||||
manufacturerCode: "",
|
||||
tagMaterial: "",
|
||||
model3dFileName: "",
|
||||
model3dDataUrl: "",
|
||||
model3dNodeOffsets: {},
|
||||
model3dNodeRotations: {},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("NonIntegratedDetailClient", () => {
|
||||
let anchorClickSpy: jest.SpyInstance;
|
||||
|
||||
@ -509,4 +545,207 @@ describe("NonIntegratedDetailClient", () => {
|
||||
within(detailList).getByAltText("扶手细节图.png"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
it("persists 泡沫护角 rows after submit and reload", async () => {
|
||||
const actor = userEvent.setup();
|
||||
|
||||
saveNonIntegratedProduct(
|
||||
createProductRecord({
|
||||
id: "DNZ0001-package-corner-save",
|
||||
spu: "SZ0230",
|
||||
skuItems: [createSkuItem()],
|
||||
bomDetails: [
|
||||
{
|
||||
styleKey: "K01",
|
||||
rows: [],
|
||||
hiddenRowIds: [],
|
||||
syntheticUnitPriceOverrides: {},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const { unmount } = render(
|
||||
<NonIntegratedDetailClient
|
||||
mode="edit"
|
||||
productId="DNZ0001-package-corner-save"
|
||||
/>,
|
||||
);
|
||||
|
||||
await actor.click(screen.getByRole("button", { name: "新增泡沫护角" }));
|
||||
|
||||
const packageHeading = await screen.findByRole("heading", {
|
||||
name: "泡沫护角",
|
||||
});
|
||||
const packageSection = packageHeading.closest("div.overflow-hidden");
|
||||
expect(packageSection).not.toBeNull();
|
||||
|
||||
const rowFields = packageSection!.querySelectorAll<
|
||||
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||
>("tbody tr input, tbody tr textarea, tbody tr select");
|
||||
expect(rowFields.length).toBeGreaterThanOrEqual(9);
|
||||
|
||||
await actor.clear(rowFields[0]!);
|
||||
await actor.type(rowFields[0]!, "BM-HJ-001");
|
||||
await actor.clear(rowFields[1]!);
|
||||
await actor.type(rowFields[1]!, "泡沫护角");
|
||||
await actor.clear(rowFields[3]!);
|
||||
await actor.type(rowFields[3]!, "泡沫");
|
||||
await actor.clear(rowFields[4]!);
|
||||
await actor.type(rowFields[4]!, "0.06");
|
||||
await actor.clear(rowFields[5]!);
|
||||
await actor.type(rowFields[5]!, "4");
|
||||
await actor.clear(rowFields[7]!);
|
||||
await actor.type(rowFields[7]!, "2");
|
||||
|
||||
await actor.click(screen.getByRole("button", { name: "提交完成" }));
|
||||
|
||||
await waitFor(() => {
|
||||
const savedProduct = getNonIntegratedProductById(
|
||||
"DNZ0001-package-corner-save",
|
||||
);
|
||||
const sharedDetail = savedProduct?.bomDetails.find(
|
||||
(detail) => detail.styleKey === "K01" && !detail.skuId,
|
||||
);
|
||||
const cornerRow = sharedDetail?.rows.find(
|
||||
(row) => row.packageSubcategory === "泡沫护角",
|
||||
);
|
||||
|
||||
expect(cornerRow).toBeDefined();
|
||||
expect(cornerRow?.partCode).toBe("BM-HJ-001");
|
||||
expect(cornerRow?.partName).toBe("泡沫护角");
|
||||
expect(cornerRow?.material).toBe("泡沫");
|
||||
expect(cornerRow?.length).toBe("0.06");
|
||||
expect(cornerRow?.quantity).toBe("4");
|
||||
expect(cornerRow?.unitPrice).toBe("2");
|
||||
});
|
||||
|
||||
unmount();
|
||||
|
||||
render(
|
||||
<NonIntegratedDetailClient
|
||||
mode="edit"
|
||||
productId="DNZ0001-package-corner-save"
|
||||
/>,
|
||||
);
|
||||
|
||||
const reloadedHeading = await screen.findByRole("heading", {
|
||||
name: "泡沫护角",
|
||||
});
|
||||
const reloadedSection = reloadedHeading.closest("div.overflow-hidden");
|
||||
expect(reloadedSection).not.toBeNull();
|
||||
expect(within(reloadedSection!).getByText("当前 1 行")).toBeInTheDocument();
|
||||
|
||||
const reloadedFields = reloadedSection!.querySelectorAll<
|
||||
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||
>("tbody tr input, tbody tr textarea, tbody tr select");
|
||||
expect(reloadedFields[0]).toHaveValue("BM-HJ-001");
|
||||
expect(reloadedFields[1]).toHaveValue("泡沫护角");
|
||||
expect(reloadedFields[3]).toHaveValue("泡沫");
|
||||
expect(reloadedFields[4]).toHaveValue("0.06");
|
||||
expect(reloadedFields[5]).toHaveValue("4");
|
||||
expect(reloadedFields[7]).toHaveValue("2");
|
||||
});
|
||||
it("keeps sku override rows isolated when adding package corner shared rows", async () => {
|
||||
const actor = userEvent.setup();
|
||||
|
||||
saveNonIntegratedProduct(
|
||||
createProductRecord({
|
||||
id: "DNZ0001-package-corner-with-override",
|
||||
spu: "SZ0230",
|
||||
skuItems: [createSkuItem()],
|
||||
bomDetails: [
|
||||
{
|
||||
styleKey: "K01",
|
||||
rows: [],
|
||||
hiddenRowIds: [],
|
||||
syntheticUnitPriceOverrides: {},
|
||||
},
|
||||
{
|
||||
styleKey: "K01",
|
||||
skuId: "sku-test-1",
|
||||
rows: [
|
||||
{
|
||||
id: "override-steel-row-1",
|
||||
category: "钢架",
|
||||
partCode: "GJ-001",
|
||||
partImage: "",
|
||||
partName: "桌腿",
|
||||
structure: "承重",
|
||||
material: "方管 20×20×0.6",
|
||||
process: "",
|
||||
remark: "",
|
||||
specSize: "",
|
||||
length: "700",
|
||||
width: "",
|
||||
longEdgeCount: "",
|
||||
shortEdgeCount: "",
|
||||
unit: "米",
|
||||
factor: "1",
|
||||
unitPrice: "1.16",
|
||||
unitWeight: "0.38",
|
||||
quantity: "1",
|
||||
},
|
||||
],
|
||||
hiddenRowIds: [],
|
||||
syntheticUnitPriceOverrides: {},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
render(
|
||||
<NonIntegratedDetailClient
|
||||
mode="edit"
|
||||
productId="DNZ0001-package-corner-with-override"
|
||||
/>,
|
||||
);
|
||||
|
||||
await actor.click(screen.getByRole("button", { name: "新增泡沫护角" }));
|
||||
|
||||
const packageHeading = await screen.findByRole("heading", {
|
||||
name: "泡沫护角",
|
||||
});
|
||||
const packageSection = packageHeading.closest("div.overflow-hidden");
|
||||
expect(packageSection).not.toBeNull();
|
||||
|
||||
const rowFields = packageSection!.querySelectorAll<
|
||||
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||
>("tbody tr input, tbody tr textarea, tbody tr select");
|
||||
expect(rowFields.length).toBeGreaterThanOrEqual(9);
|
||||
|
||||
await actor.clear(rowFields[1]!);
|
||||
await actor.type(rowFields[1]!, "泡沫护角");
|
||||
await actor.clear(rowFields[3]!);
|
||||
await actor.type(rowFields[3]!, "泡沫");
|
||||
await actor.clear(rowFields[4]!);
|
||||
await actor.type(rowFields[4]!, "0.06");
|
||||
await actor.clear(rowFields[5]!);
|
||||
await actor.type(rowFields[5]!, "4");
|
||||
await actor.clear(rowFields[7]!);
|
||||
await actor.type(rowFields[7]!, "2");
|
||||
|
||||
await actor.click(screen.getByRole("button", { name: "提交完成" }));
|
||||
|
||||
await waitFor(() => {
|
||||
const savedProduct = getNonIntegratedProductById(
|
||||
"DNZ0001-package-corner-with-override",
|
||||
);
|
||||
const sharedDetail = savedProduct?.bomDetails.find(
|
||||
(detail) => detail.styleKey === "K01" && !detail.skuId,
|
||||
);
|
||||
const overrideDetail = savedProduct?.bomDetails.find(
|
||||
(detail) => detail.styleKey === "K01" && detail.skuId === "sku-test-1",
|
||||
);
|
||||
|
||||
expect(
|
||||
sharedDetail?.rows.some((row) => row.id === "override-steel-row-1"),
|
||||
).toBe(false);
|
||||
expect(
|
||||
overrideDetail?.rows.some((row) => row.id === "override-steel-row-1"),
|
||||
).toBe(true);
|
||||
expect(
|
||||
sharedDetail?.rows.some((row) => row.packageSubcategory === "泡沫护角"),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -118,6 +118,7 @@ import {
|
||||
type NonIntegratedStyleDetail,
|
||||
type NonIntegratedUploadFile,
|
||||
} from "./nonIntegratedStorage";
|
||||
import { saveSharedNonIntegratedProduct } from "./nonIntegratedApi";
|
||||
|
||||
type BasicFieldType = "text" | "textarea" | "select";
|
||||
type SkuTabKey = "skuInfo" | "fileUpload";
|
||||
@ -4260,9 +4261,11 @@ function buildProductPayload(
|
||||
export default function NonIntegratedDetailClient({
|
||||
mode,
|
||||
productId,
|
||||
initialProduct = null,
|
||||
}: {
|
||||
mode: "create" | "edit" | "view";
|
||||
productId?: string;
|
||||
initialProduct?: NonIntegratedProductRecord | null;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const previewFileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
@ -4285,7 +4288,9 @@ export default function NonIntegratedDetailClient({
|
||||
const model3dInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const existingProduct =
|
||||
mode !== "create" && productId
|
||||
? getNonIntegratedProductById(productId)
|
||||
? shouldUseRemoteAssetStorage()
|
||||
? initialProduct
|
||||
: getNonIntegratedProductById(productId)
|
||||
: null;
|
||||
const initialBomDetailsResultRef = useRef<InitialBomDetailsResult | null>(null);
|
||||
if (!initialBomDetailsResultRef.current) {
|
||||
@ -5816,7 +5821,11 @@ export default function NonIntegratedDetailClient({
|
||||
),
|
||||
);
|
||||
|
||||
if (shouldUseRemoteAssetStorage()) {
|
||||
await saveSharedNonIntegratedProduct(nextProduct);
|
||||
} else {
|
||||
saveNonIntegratedProduct(nextProduct);
|
||||
}
|
||||
setSaveMessage(successMessage);
|
||||
setSaveMessageType("success");
|
||||
return true;
|
||||
@ -6616,37 +6625,6 @@ export default function NonIntegratedDetailClient({
|
||||
setSaveMessageType("success");
|
||||
}
|
||||
|
||||
const updateCurrentBomRows = useCallback(
|
||||
(updater: (rows: NonIntegratedBomRow[]) => NonIntegratedBomRow[]) => {
|
||||
if (isReadOnly || !selectedBomStyleKey) return;
|
||||
|
||||
setBomDetails((current) => {
|
||||
const sharedDetail =
|
||||
current[selectedBomStyleKey] ?? createEmptyBomDetailState();
|
||||
const sharedRows = sharedDetail.rows;
|
||||
const currentSkuRows =
|
||||
selectedBomSkuKey && hasBomDetailOverride(current, selectedBomSkuKey)
|
||||
? (current[selectedBomSkuKey]?.rows ?? [])
|
||||
: [];
|
||||
const baseRows =
|
||||
currentSkuRows.length > 0
|
||||
? mergeBomRows(sharedRows, currentSkuRows)
|
||||
: sharedRows;
|
||||
const nextRows = updater(cloneBomRows(baseRows));
|
||||
return {
|
||||
...current,
|
||||
[selectedBomStyleKey]: {
|
||||
...sharedDetail,
|
||||
rows: cloneBomRows(nextRows),
|
||||
},
|
||||
};
|
||||
});
|
||||
setSaveMessage("");
|
||||
setSaveMessageType("success");
|
||||
},
|
||||
[isReadOnly, selectedBomSkuKey, selectedBomStyleKey],
|
||||
);
|
||||
|
||||
function resolveNextBomRow(
|
||||
row: NonIntegratedBomRow,
|
||||
key: BomRowFieldKey,
|
||||
@ -6687,8 +6665,23 @@ export default function NonIntegratedDetailClient({
|
||||
|
||||
let appendedRowId = "";
|
||||
|
||||
updateCurrentBomRows((current) => {
|
||||
const scopedRows = current.filter(
|
||||
setBomDetails((current) => {
|
||||
const sharedDetail =
|
||||
current[selectedBomStyleKey] ?? createEmptyBomDetailState();
|
||||
const sharedRows = sharedDetail.rows;
|
||||
const currentOverrideRows =
|
||||
selectedBomSkuKey && hasBomDetailOverride(current, selectedBomSkuKey)
|
||||
? (current[selectedBomSkuKey]?.rows ?? [])
|
||||
: [];
|
||||
const currentHiddenRowIds =
|
||||
selectedBomSkuKey && hasBomDetailOverride(current, selectedBomSkuKey)
|
||||
? (current[selectedBomSkuKey]?.hiddenRowIds ?? [])
|
||||
: [];
|
||||
const visibleRows =
|
||||
currentOverrideRows.length > 0 || currentHiddenRowIds.length > 0
|
||||
? mergeBomRows(sharedRows, currentOverrideRows, currentHiddenRowIds)
|
||||
: cloneBomRows(sharedRows);
|
||||
const scopedVisibleRows = visibleRows.filter(
|
||||
(row) =>
|
||||
getBomRowCategory(row) === category &&
|
||||
(category !== "包材" ||
|
||||
@ -6696,18 +6689,29 @@ export default function NonIntegratedDetailClient({
|
||||
getBomRowPackageSubcategory(row) === packageSubcategory),
|
||||
);
|
||||
const previousRow =
|
||||
scopedRows.length > 0 ? scopedRows[scopedRows.length - 1] : null;
|
||||
scopedVisibleRows.length > 0
|
||||
? scopedVisibleRows[scopedVisibleRows.length - 1]
|
||||
: null;
|
||||
const nextRow = applyBomRowLibraryMetrics(
|
||||
createNextBomRowFromPrevious(previousRow, category, packageSubcategory),
|
||||
);
|
||||
appendedRowId = nextRow.id;
|
||||
return [...current, nextRow];
|
||||
|
||||
return {
|
||||
...current,
|
||||
[selectedBomStyleKey]: {
|
||||
...sharedDetail,
|
||||
rows: [...sharedRows, nextRow],
|
||||
},
|
||||
};
|
||||
});
|
||||
setActiveSharedBomRowIds((current) =>
|
||||
appendedRowId
|
||||
? Array.from(new Set([...current, appendedRowId]))
|
||||
: current,
|
||||
);
|
||||
setSaveMessage("");
|
||||
setSaveMessageType("success");
|
||||
}
|
||||
|
||||
function updateBomRow(rowId: string, key: BomRowFieldKey, value: string) {
|
||||
@ -8451,7 +8455,9 @@ export default function NonIntegratedDetailClient({
|
||||
documentWorkspaceConfirmations,
|
||||
),
|
||||
);
|
||||
const savedProduct = saveNonIntegratedProduct(normalizedProduct);
|
||||
const savedProduct = shouldUseRemoteAssetStorage()
|
||||
? await saveSharedNonIntegratedProduct(normalizedProduct)
|
||||
: saveNonIntegratedProduct(normalizedProduct);
|
||||
router.push(
|
||||
`/intake/non-integrated/${encodeURIComponent(savedProduct.id)}`,
|
||||
);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { requireSession } from '@/lib/server-session'
|
||||
import { intakeNonIntegratedProductService } from '@/server/services'
|
||||
import NonIntegratedDetailClient from '../NonIntegratedDetailClient'
|
||||
|
||||
export default async function NonIntegratedDetailPage({
|
||||
@ -8,10 +9,18 @@ export default async function NonIntegratedDetailPage({
|
||||
params: Promise<{ id: string }>
|
||||
searchParams: Promise<{ mode?: string }>
|
||||
}) {
|
||||
await requireSession('/login')
|
||||
const { authUser } = await requireSession('/login')
|
||||
const { id } = await params
|
||||
const resolvedSearchParams = await searchParams
|
||||
const mode = resolvedSearchParams.mode === 'view' ? 'view' : 'edit'
|
||||
const decodedId = decodeURIComponent(id)
|
||||
const initialProduct = await intakeNonIntegratedProductService.getById(decodedId, authUser)
|
||||
|
||||
return <NonIntegratedDetailClient mode={mode} productId={decodeURIComponent(id)} />
|
||||
return (
|
||||
<NonIntegratedDetailClient
|
||||
mode={mode}
|
||||
productId={decodedId}
|
||||
initialProduct={initialProduct}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -28,6 +28,16 @@ export async function listSharedNonIntegratedProducts() {
|
||||
return data.products
|
||||
}
|
||||
|
||||
export async function getSharedNonIntegratedProductById(id: string) {
|
||||
const response = await fetch(`/api/intake/non-integrated/${encodeURIComponent(id)}`, {
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
})
|
||||
|
||||
const data = await readApiData<{ product: NonIntegratedProductRecord }>(response)
|
||||
return data.product
|
||||
}
|
||||
|
||||
export async function saveSharedNonIntegratedProduct(product: NonIntegratedProductRecord) {
|
||||
const response = await fetch('/api/intake/non-integrated', {
|
||||
method: 'POST',
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
categoryService,
|
||||
draftGroupService,
|
||||
intakeService,
|
||||
intakeNonIntegratedProductService,
|
||||
materialService,
|
||||
warehouseService,
|
||||
} from '@/server/services'
|
||||
@ -45,7 +46,7 @@ export default async function IntakePage({
|
||||
}: {
|
||||
searchParams: Promise<Record<string, string | string[] | undefined>>
|
||||
}) {
|
||||
const { user } = await requireSession('/login')
|
||||
const { user, authUser } = await requireSession('/login')
|
||||
const params = await searchParams
|
||||
const draftGroupId = typeof params.draftGroupId === 'string' ? params.draftGroupId : null
|
||||
|
||||
@ -56,6 +57,7 @@ export default async function IntakePage({
|
||||
materialLibraryFinishes,
|
||||
materialLibraryCategories,
|
||||
materialLibraryDictionaries,
|
||||
initialNonIntegratedProducts,
|
||||
] = await Promise.all([
|
||||
draftGroupService.list(),
|
||||
warehouseService.listLocations(),
|
||||
@ -63,6 +65,7 @@ export default async function IntakePage({
|
||||
materialService.findAll({ limit: 500 }),
|
||||
materialService.findAllCategories(),
|
||||
assetCenterService.getSubstrateDictionaryBundle(),
|
||||
intakeNonIntegratedProductService.list(authUser),
|
||||
])
|
||||
|
||||
const eligibleDraftGroups = draftGroups.filter((group) =>
|
||||
@ -128,6 +131,7 @@ export default async function IntakePage({
|
||||
materialLibraryFinishes={materialLibraryFinishes}
|
||||
materialLibraryCategories={materialLibraryCategories}
|
||||
materialLibraryDictionaries={materialLibraryDictionaries}
|
||||
initialNonIntegratedProducts={initialNonIntegratedProducts}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -145,6 +149,7 @@ export default async function IntakePage({
|
||||
materialLibraryFinishes={materialLibraryFinishes}
|
||||
materialLibraryCategories={materialLibraryCategories}
|
||||
materialLibraryDictionaries={materialLibraryDictionaries}
|
||||
initialNonIntegratedProducts={initialNonIntegratedProducts}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user