From 5e5687dcc1bdf12fcbf5123acbf45fcf07cdddf9 Mon Sep 17 00:00:00 2001 From: WuYongJie Date: Fri, 12 Jun 2026 17:16:47 +0800 Subject: [PATCH] fix: require manual confirmation for factory workflows --- .../mobile/AssetCenterMobileView.tsx | 2 +- .../mobile/AssistedAssemblyMobileView.tsx | 5 +- .../NonIntegratedDetailClient.test.tsx | 130 ++++++++++++++++-- .../NonIntegratedDetailClient.tsx | 50 +++++-- .../nonIntegratedDetailTypes.ts | 4 + ...tegratedWorkflowAutoConfirmationEffects.ts | 63 --------- .../useNonIntegratedWorkflowCompletion.ts | 21 ++- ...eNonIntegratedWorkflowConfirmationState.ts | 56 ++++---- .../useNonIntegratedWorkflowRows.ts | 2 +- .../workspaces/SampleMeasurementWorkspace.tsx | 6 +- .../basic-info/productFactoryHelpers.ts | 43 +++++- .../workspaces/basic-info/skuFieldConfig.ts | 5 +- .../documents/documentWorkspaceConfig.ts | 2 - .../ProductFactoryHeaderActions.tsx | 9 +- .../ProductFactoryInfoTable.tsx | 14 +- .../ProductFactorySkuInfoTable.tsx | 14 +- .../productFactoryTargetSkuHelpers.ts | 5 + .../useProductFactoryRowController.ts | 121 ++++++++++++---- .../useProductWorkflowRowController.ts | 30 +++- .../SampleMeasurementHeaderActions.tsx | 9 +- .../sample-workflow/sampleWorkflowHelpers.ts | 2 +- .../useSampleWorkflowStatusController.ts | 83 ++++++++++- .../DocumentWorkspaceHeaderActionsSlot.tsx | 4 + .../shared/DocumentWorkspaceSidebar.tsx | 17 ++- .../src/app/mobile/HomeMobileView.tsx | 16 ++- 25 files changed, 524 insertions(+), 189 deletions(-) diff --git a/furniture-3d-system/src/app/asset-center/mobile/AssetCenterMobileView.tsx b/furniture-3d-system/src/app/asset-center/mobile/AssetCenterMobileView.tsx index 128839ea..e4495d12 100644 --- a/furniture-3d-system/src/app/asset-center/mobile/AssetCenterMobileView.tsx +++ b/furniture-3d-system/src/app/asset-center/mobile/AssetCenterMobileView.tsx @@ -30,7 +30,7 @@ interface AssetFamilyDTO { name: string code?: string type: AssetTypeFilter - assetSeriesId: string + assetSeriesId: string | null metadata?: unknown _count?: { variants: number; skuGroups: number } skuGroups?: Array<{ id: string }> diff --git a/furniture-3d-system/src/app/assisted-assembly/mobile/AssistedAssemblyMobileView.tsx b/furniture-3d-system/src/app/assisted-assembly/mobile/AssistedAssemblyMobileView.tsx index 68d45e5e..7bbf75b2 100644 --- a/furniture-3d-system/src/app/assisted-assembly/mobile/AssistedAssemblyMobileView.tsx +++ b/furniture-3d-system/src/app/assisted-assembly/mobile/AssistedAssemblyMobileView.tsx @@ -18,8 +18,7 @@ interface VariantDTO { } interface AssemblyWorkspaceSnapshot { - assetSeriesId?: string - [key: string]: unknown + assetSeriesId?: string | null } type InspectorTabKey = 'assembly' | 'ai' @@ -86,7 +85,7 @@ export default function AssistedAssemblyMobileView({ {/* 系列列表(未选择时显示) */} - {!selectedSeriesId && assetSeries.length > 0 && ( + {!selectedAssetSeriesId && assetSeries.length > 0 && (
{assetSeries.map((series) => ( diff --git a/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.test.tsx b/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.test.tsx index 1109f218..718d866c 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.test.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.test.tsx @@ -34,6 +34,7 @@ import { NON_INTEGRATED_DOCUMENT_WORKSPACE_CATEGORIES, replaceNonIntegratedProducts, saveNonIntegratedProduct, + type NonIntegratedFactoryRow, type NonIntegratedSkuItem, type NonIntegratedProductRecord, } from "./nonIntegratedStorage"; @@ -454,6 +455,38 @@ function createSkuItem( }; } +function createFactoryRow( + overrides: Partial = {}, +): NonIntegratedFactoryRow { + return { + skuId: "sku-test-1", + confirmed: false, + model: "SZ0230K01", + skuCode: "SZ023001", + setCount: "1", + barcode: "SZ023001N1280V01", + specName: "款式一 140/60/75", + size: "140/60/75", + color: "胡桃色", + material: "橡胶木", + installMethod: "拆装", + supplierName: "兰考华兰家具", + supplierCode: "N1280", + warehouse: "郑州仓", + shippingMethod: "入仓", + minimumOrderQuantity: "50", + taxIncludedUnitPrice: "", + leadTime: "30天", + assemblyMode: "", + productionType: "OEM", + installationService: "送货", + supplierCategory: "主力供应商", + purchaseOrderMethod: "订单式", + executeStandard: "GB/T 3324", + ...overrides, + }; +} + function createMaterialFinish( overrides: Partial & Pick, ): MaterialDTO { @@ -4446,7 +4479,7 @@ describe("NonIntegratedDetailClient", () => { }); }); - it("shares product factory selection fields by model without changing SKU content", async () => { + it("shares product factory selection fields by SPU without changing SKU content", async () => { const actor = userEvent.setup(); saveNonIntegratedProduct( @@ -4573,12 +4606,12 @@ describe("NonIntegratedDetailClient", () => { expect(savedProduct?.factoryRows.map((row) => row.warehouse)).toEqual([ "郑州仓", "郑州仓", - "", + "郑州仓", ]); expect(savedProduct?.factoryRows.map((row) => row.shippingMethod)).toEqual([ "代发", "代发", - "", + "代发", ]); expect( savedProduct?.factoryRows.map((row) => row.minimumOrderQuantity), @@ -4586,7 +4619,7 @@ describe("NonIntegratedDetailClient", () => { expect(savedProduct?.factoryRows.map((row) => row.productionType)).toEqual([ "OEM", "OEM", - "", + "OEM", ]); expect( savedProduct?.factoryRows.map((row) => row.installationService), @@ -4594,11 +4627,11 @@ describe("NonIntegratedDetailClient", () => { expect(savedProduct?.factoryRows.map((row) => row.supplierCategory)).toEqual([ "主力供应商", "主力供应商", - "", + "主力供应商", ]); expect( savedProduct?.factoryRows.map((row) => row.purchaseOrderMethod), - ).toEqual(["订单式", "订单式", ""]); + ).toEqual(["订单式", "订单式", "订单式"]); expect(savedProduct?.skuItems.map((item) => item.manufacturer)).toEqual([ "曹县嘉欢", "曹县嘉欢", @@ -4640,17 +4673,17 @@ describe("NonIntegratedDetailClient", () => { expect(savedProduct?.factoryRows.map((row) => row.warehouse)).toEqual([ "杭州仓", "杭州仓", - "", + "杭州仓", ]); expect(savedProduct?.factoryRows.map((row) => row.shippingMethod)).toEqual([ "入仓", "入仓", - "", + "入仓", ]); expect(savedProduct?.factoryRows.map((row) => row.productionType)).toEqual([ "ODM", "ODM", - "", + "ODM", ]); expect(savedProduct?.skuItems.map((item) => item.manufacturer)).toEqual([ "曹县嘉欢", @@ -4679,16 +4712,82 @@ describe("NonIntegratedDetailClient", () => { expect(savedProduct?.factoryRows.map((row) => row.shippingMethod)).toEqual([ "入仓", "入仓", - "", + "入仓", ]); expect(savedProduct?.factoryRows.map((row) => row.productionType)).toEqual([ "ODM", "ODM", - "", + "ODM", ]); }); }); + it("requires product factory rows to be manually confirmed after all fields are filled", async () => { + const actor = userEvent.setup(); + const productId = "SZ0230-product-factory-manual-confirm"; + + saveNonIntegratedProduct( + createProductRecord({ + id: productId, + spu: "SZ0230", + skuBatchConfirmedAt: CONFIRMED_SKU_BATCH_AT, + skuItems: [ + createSkuItem({ + id: "sku-factory-confirm-1", + model: "SZ0230K01", + skuCode: "SZ023001", + barcode: "SZ023001N1280V01", + manufacturer: "兰考华兰家具", + manufacturerCode: "N1280", + installMethod: "拆装", + executeStandard: "GB/T 3324", + }), + ], + factoryRows: [ + createFactoryRow({ + skuId: "sku-factory-confirm-1", + confirmed: false, + model: "SZ0230K01", + skuCode: "SZ023001", + barcode: "SZ023001N1280V01", + }), + ], + documentWorkspaceConfirmations: + createDefaultDocumentWorkspaceConfirmations(false), + }), + ); + + render( + , + ); + + await actor.click(screen.getByTestId("document-workspace-trigger-产品定厂")); + + const confirmButton = screen.getByTestId("product-factory-confirm"); + expect(confirmButton).toHaveTextContent("资料已填完核对无误请确认。"); + + await waitFor(() => { + const savedProduct = getNonIntegratedProductById(productId); + expect(savedProduct?.factoryRows?.[0]?.confirmed).toBe(false); + expect(savedProduct?.documentWorkspaceConfirmations?.产品定厂).not.toBe( + true, + ); + expect(savedProduct?.documentWorkspaceApprovedAt?.产品定厂).toBeUndefined(); + }); + + await actor.click(confirmButton); + + await waitFor(() => { + const savedProduct = getNonIntegratedProductById(productId); + expect(savedProduct?.factoryRows?.[0]?.confirmed).toBe(true); + expect(savedProduct?.documentWorkspaceConfirmations?.产品定厂).toBe(true); + expect(savedProduct?.documentWorkspaceApprovedAt?.产品定厂).toEqual( + expect.any(String), + ); + }); + expect(confirmButton).toHaveTextContent("已确认"); + }); + it("persists the SKU national standard compliance selection", async () => { const actor = userEvent.setup(); @@ -6447,8 +6546,16 @@ describe("NonIntegratedDetailClient", () => { weightKg: "18.5", }, ]); + expect(savedProduct?.documentWorkspaceConfirmations?.样包测量).not.toBe( + true, + ); + expect(savedProduct?.documentWorkspaceApprovedAt?.样包测量).toBeUndefined(); }); + const confirmButton = screen.getByTestId("sample-measurement-confirm"); + expect(confirmButton).toHaveTextContent("资料已填完核对无误请确认。"); + await actor.click(confirmButton); + await waitFor(() => { const savedProduct = getNonIntegratedProductById( "SZ0230-sample-measurement", @@ -6458,6 +6565,7 @@ describe("NonIntegratedDetailClient", () => { expect.any(String), ); }); + expect(confirmButton).toHaveTextContent("已确认"); expect( screen.getByTestId("sample-measurement-lengthCm-sku-sample-1"), diff --git a/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.tsx b/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.tsx index e5c185ef..bebf75b2 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedDetailClient.tsx @@ -1370,7 +1370,9 @@ export default function NonIntegratedDetailClient({ isReadOnly || isSeriesSkuComponentAggregateMode; const { allFinishedSampleShipmentRowsComplete, + allProductFactoryRowsConfirmed, allProductFactoryRowsComplete, + allSampleMeasurementRowsConfirmedByPricing, allSampleMeasurementRowsComplete, confirmedProductFactoryRowCount, displayedFinishedSampleShipmentRowsComplete, @@ -1383,6 +1385,7 @@ export default function NonIntegratedDetailClient({ displayedProductPricingRowsAllSystemEntered, displayedProductPricingRowsComplete, displayedProductPricingRowsReadyForPush, + displayedSampleMeasurementRowsConfirmedByPricing, displayedSampleMeasurementRowsComplete, isSampleMeasurementSkuLocked, planningLayoutHasContent, @@ -1460,6 +1463,9 @@ export default function NonIntegratedDetailClient({ const activeDocumentWorkspaceHasContent = (documentWorkspaceCountByCategory[activeDocumentWorkspaceCategory] ?? 0) > 0; + const displayedSampleMeasurementHasPendingRows = + displayedProductSampleMeasurementRows.length > 0 && + !displayedSampleMeasurementRowsComplete; const { basicInfoDisplayCount, basicInfoIsApproved, @@ -1476,19 +1482,19 @@ export default function NonIntegratedDetailClient({ isWorkflowNodeConfirmed, } = useNonIntegratedWorkflowConfirmationState({ allFinishedSampleShipmentRowsComplete, - allProductFactoryRowsComplete, - allSampleMeasurementRowsComplete, + allProductFactoryRowsConfirmed, + allSampleMeasurementRowsConfirmedByPricing, basicInfoHasContent, displayedFinishedSampleShipmentRowsComplete, displayedFirstQualityRowsComplete, displayedProductFactoryIsConfirmed, displayedProductLimitRowsComplete, displayedProductSampleMeasurementRows, + displayedSampleMeasurementRowsConfirmedByPricing, displayedSampleMeasurementRowsComplete, documentWorkspaceApprovedAt, documentWorkspaceConfirmations, documentWorkspaceRevisionSignatures, - isSampleMeasurementSkuLocked, previousDocumentWorkspaceRevisionSignatures: documentWorkspaceRevisionSignatureRef.current, productPricingCanBeConfirmed, @@ -1498,20 +1504,13 @@ export default function NonIntegratedDetailClient({ }); useNonIntegratedWorkflowAutoConfirmationEffects({ allFinishedSampleShipmentRowsComplete, - allProductFactoryRowsComplete, - allSampleMeasurementRowsComplete, existingProductCreatedAt, - factoryRows, finishedSampleShipmentRows, isReadOnly, mode, - productFactoryRows, productId, - sampleMeasurementRows, setDocumentWorkspaceApprovedAt, setDocumentWorkspaceConfirmations, - setFactoryRows, - supplierCodeByName, }); useNonIntegratedWorkflowRevisionResetEffect({ documentWorkspaceRevisionSignatureRef, @@ -1803,6 +1802,7 @@ export default function NonIntegratedDetailClient({ documentWorkspaceConfirmations; const nextDocumentWorkspaceApprovedAt = overrides?.documentWorkspaceApprovedAt ?? documentWorkspaceApprovedAt; + const nextFactoryRows = overrides?.factoryRows ?? productFactoryRows; const nextReviewRounds = overrides?.reviewRounds ?? reviewRounds; const nextFinishedSampleShipmentRows = overrides?.finishedSampleShipmentRows ?? productFinishedSampleShipmentRows; @@ -1810,6 +1810,8 @@ export default function NonIntegratedDetailClient({ overrides?.openingReference ?? openingReferenceRef.current; const nextSkuItems = overrides?.skuItems ?? skuItems; const nextPricingRows = overrides?.pricingRows ?? productPricingRows; + const nextSampleMeasurementRows = + overrides?.sampleMeasurementRows ?? productSampleMeasurementRows; const nextSkuBatchCount = overrides?.skuBatchCount ?? skuBatchCount; const nextSkuBatchConfirmedAt = overrides?.skuBatchConfirmedAt ?? skuBatchConfirmedAt; @@ -1874,11 +1876,11 @@ export default function NonIntegratedDetailClient({ bomDetails, nextReviewRounds, qualityInspectionSheet, - productFactoryRows, + nextFactoryRows, nextPricingRows, nextPlanningLayoutRows, productLimitRows, - productSampleMeasurementRows, + nextSampleMeasurementRows, nextFinishedSampleShipmentRows, productFirstQualityRows, uploadFiles, @@ -2585,13 +2587,16 @@ export default function NonIntegratedDetailClient({ derivedSkuItems, displayedProductFactoryHasPendingRows, displayedProductFactoryHasSystemEnteredPricingRows, + displayedProductFactoryIsConfirmed, displayedProductFactoryRows, displayedProductFactoryRowsComplete, documentWorkspaceApprovedAt, documentWorkspaceConfirmations, + documentWorkspaceRevisionSignatureRef, isCurrentUserAdmin, isReadOnly, isSkuItemFactoryConfirmed, + persistEditedProduct, productFactoryModelAutofillRef, productFactoryReopenField, productFactoryRows, @@ -2631,7 +2636,7 @@ export default function NonIntegratedDetailClient({ documentWorkspaceApprovedAt, documentWorkspaceConfirmations, documentWorkspaceRevisionSignatureRef, - isProductFactorySkuComplete, + isProductFactorySkuConfirmed: isSkuItemFactoryConfirmed, isProductLimitNodeLocked, isProductPricingNodeLocked, isReadOnly, @@ -2643,6 +2648,8 @@ export default function NonIntegratedDetailClient({ planningLayoutRowsRef, pricingRows, productId, + productSampleMeasurementRows, + sampleMeasurementRows, scheduleAutoSave, setDocumentWorkspaceApprovedAt, setDocumentWorkspaceConfirmations, @@ -2879,11 +2886,20 @@ export default function NonIntegratedDetailClient({ handleFinishedSampleShipmentStatusClick, handleSampleMeasurementStatusClick, } = useSampleWorkflowStatusController({ + documentWorkspaceApprovedAt, + documentWorkspaceConfirmations, + documentWorkspaceRevisionSignatureRef, displayedFinishedSampleShipmentRows, displayedFinishedSampleShipmentRowsComplete, displayedProductFactoryIsConfirmed, displayedProductSampleMeasurementRows, + displayedSampleMeasurementIsConfirmed, displayedSampleMeasurementRowsComplete, + isReadOnly, + persistEditedProduct, + productSampleMeasurementRows, + setDocumentWorkspaceApprovedAt, + setDocumentWorkspaceConfirmations, setSaveMessage, setSaveMessageType, }); @@ -3820,7 +3836,7 @@ export default function NonIntegratedDetailClient({ allRows: productSampleMeasurementRows, displayedRows: displayedProductSampleMeasurementRows, isReadOnly, - isProductFactorySkuComplete, + isProductFactorySkuConfirmed: isSkuItemFactoryConfirmed, isSampleMeasurementSkuLocked, onUpdateRow: updateSampleMeasurementRow, }, @@ -4320,9 +4336,12 @@ export default function NonIntegratedDetailClient({ documentWorkspaceCountByCategory } documentWorkspaceView={documentWorkspaceView} - isProductFactoryRowComplete={isProductFactoryRowComplete} + isSampleMeasurementConfirmed={ + displayedSampleMeasurementIsConfirmed + } isWorkflowNodeConfirmed={isWorkflowNodeConfirmed} productFactoryRows={productFactoryRows} + sampleMeasurementRows={productSampleMeasurementRows} visibleGroupedUploadFiles={visibleGroupedUploadFiles} onOpenBasicInfo={() => setDocumentWorkspaceView("detail")} onOpenDocumentWorkspace={handleOpenDocumentWorkspace} @@ -4781,6 +4800,7 @@ export default function NonIntegratedDetailClient({ displayedProductPricingRowsReadyForPush, displayedProductSampleMeasurementRowCount: displayedProductSampleMeasurementRows.length, + displayedSampleMeasurementHasPendingRows, displayedSampleMeasurementIsConfirmed, documentWorkspaceConfirmationAction: renderDocumentWorkspaceConfirmationAction(), diff --git a/furniture-3d-system/src/app/intake/non-integrated/nonIntegratedDetailTypes.ts b/furniture-3d-system/src/app/intake/non-integrated/nonIntegratedDetailTypes.ts index 0f58be86..f66c6dc1 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/nonIntegratedDetailTypes.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/nonIntegratedDetailTypes.ts @@ -2,6 +2,7 @@ import type { MaterialDTO } from "@/server/services"; import type { NonIntegratedDocumentWorkspaceApprovalMap, NonIntegratedDocumentWorkspaceConfirmationMap, + NonIntegratedFactoryRow, NonIntegratedFinishedSampleShipmentRow, NonIntegratedOpeningReference, NonIntegratedProductRecord, @@ -11,6 +12,7 @@ import type { NonIntegratedReviewIssue, NonIntegratedReviewRound, NonIntegratedReviewSheet, + NonIntegratedSampleMeasurementRow, NonIntegratedSkuItem, } from "./core/nonIntegratedStorage"; import type { @@ -58,6 +60,8 @@ export type AutoSaveNoticeType = "saving" | "success" | "error"; export type PersistEditedProductOverrides = { documentWorkspaceConfirmations?: NonIntegratedDocumentWorkspaceConfirmationMap; documentWorkspaceApprovedAt?: NonIntegratedDocumentWorkspaceApprovalMap; + factoryRows?: NonIntegratedFactoryRow[]; + sampleMeasurementRows?: NonIntegratedSampleMeasurementRow[]; reviewRounds?: NonIntegratedReviewRound[]; finishedSampleShipmentRows?: NonIntegratedFinishedSampleShipmentRow[]; openingReference?: NonIntegratedOpeningReference; diff --git a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowAutoConfirmationEffects.ts b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowAutoConfirmationEffects.ts index 30fea2b2..819c4ce6 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowAutoConfirmationEffects.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowAutoConfirmationEffects.ts @@ -1,89 +1,41 @@ import { useEffect, type Dispatch, type SetStateAction } from "react"; import { FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY, - PRODUCT_FACTORY_WORKSPACE_CATEGORY, - SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY, } from "./workspaces/documents/documentWorkspaceConfig"; import type { DocumentWorkspaceCategory } from "./workspaces/documents/documentWorkspaceHelpers"; -import { - isProductFactoryRowComplete, - normalizeFactoryRowsForPayload, -} from "./workspaces/basic-info/productFactoryHelpers"; import type { NonIntegratedDocumentWorkspaceApprovalMap, NonIntegratedDocumentWorkspaceConfirmationMap, - NonIntegratedFactoryRow, NonIntegratedFinishedSampleShipmentRow, - NonIntegratedSampleMeasurementRow, } from "./core/nonIntegratedStorage"; type DetailMode = "create" | "edit" | "view"; type UseNonIntegratedWorkflowAutoConfirmationEffectsParams = { allFinishedSampleShipmentRowsComplete: boolean; - allProductFactoryRowsComplete: boolean; - allSampleMeasurementRowsComplete: boolean; existingProductCreatedAt: string; - factoryRows: NonIntegratedFactoryRow[]; finishedSampleShipmentRows: NonIntegratedFinishedSampleShipmentRow[]; isReadOnly: boolean; mode: DetailMode; - productFactoryRows: NonIntegratedFactoryRow[]; productId?: string; - sampleMeasurementRows: NonIntegratedSampleMeasurementRow[]; setDocumentWorkspaceApprovedAt: Dispatch< SetStateAction >; setDocumentWorkspaceConfirmations: Dispatch< SetStateAction >; - setFactoryRows: Dispatch>; - supplierCodeByName: Map; }; export function useNonIntegratedWorkflowAutoConfirmationEffects({ allFinishedSampleShipmentRowsComplete, - allProductFactoryRowsComplete, - allSampleMeasurementRowsComplete, existingProductCreatedAt, - factoryRows, finishedSampleShipmentRows, isReadOnly, mode, - productFactoryRows, productId, - sampleMeasurementRows, setDocumentWorkspaceApprovedAt, setDocumentWorkspaceConfirmations, - setFactoryRows, - supplierCodeByName, }: UseNonIntegratedWorkflowAutoConfirmationEffectsParams) { - useEffect(() => { - if (isReadOnly || factoryRows.length === 0) return; - - const normalizedRows = normalizeFactoryRowsForPayload( - productFactoryRows, - supplierCodeByName, - ); - const hasConfirmationChange = normalizedRows.some( - (row) => row.confirmed !== isProductFactoryRowComplete(row), - ); - if (!hasConfirmationChange) return; - - setFactoryRows( - normalizedRows.map((row) => ({ - ...row, - confirmed: isProductFactoryRowComplete(row), - })), - ); - }, [ - factoryRows.length, - isReadOnly, - productFactoryRows, - setFactoryRows, - supplierCodeByName, - ]); - useEffect(() => { if ( mode !== "edit" || @@ -99,17 +51,6 @@ export function useNonIntegratedWorkflowAutoConfirmationEffects({ touched: boolean; complete: boolean; }> = [ - { - category: PRODUCT_FACTORY_WORKSPACE_CATEGORY, - touched: factoryRows.length > 0, - complete: allProductFactoryRowsComplete, - }, - { - category: SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY, - touched: sampleMeasurementRows.length > 0, - complete: - allProductFactoryRowsComplete && allSampleMeasurementRowsComplete, - }, { category: FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY, touched: finishedSampleShipmentRows.length > 0, @@ -149,15 +90,11 @@ export function useNonIntegratedWorkflowAutoConfirmationEffects({ }); }, [ allFinishedSampleShipmentRowsComplete, - allProductFactoryRowsComplete, - allSampleMeasurementRowsComplete, existingProductCreatedAt, - factoryRows.length, finishedSampleShipmentRows.length, isReadOnly, mode, productId, - sampleMeasurementRows.length, setDocumentWorkspaceApprovedAt, setDocumentWorkspaceConfirmations, ]); diff --git a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowCompletion.ts b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowCompletion.ts index 2827725a..d582c6f3 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowCompletion.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowCompletion.ts @@ -56,7 +56,7 @@ export function useNonIntegratedWorkflowCompletion({ productSampleMeasurementRows: NonIntegratedSampleMeasurementRow[]; }) { const confirmedProductFactoryRowCount = useMemo( - () => productFactoryRows.filter(isProductFactoryRowComplete).length, + () => productFactoryRows.filter((row) => row.confirmed).length, [productFactoryRows], ); const populatedProductPricingRowCount = useMemo( @@ -109,7 +109,10 @@ export function useNonIntegratedWorkflowCompletion({ displayedProductFactoryRows.some((row) => !isProductFactoryRowComplete(row)); const displayedProductFactoryIsConfirmed = displayedProductFactoryRows.length > 0 && - !displayedProductFactoryHasPendingRows; + displayedProductFactoryRows.every((row) => row.confirmed); + const allProductFactoryRowsConfirmed = + productFactoryRows.length > 0 && + productFactoryRows.every((row) => row.confirmed); const displayedProductFactoryRowsComplete = displayedProductFactoryRows.length > 0 && displayedProductFactoryRows.every(isProductFactoryRowComplete); @@ -162,6 +165,16 @@ export function useNonIntegratedWorkflowCompletion({ ), [productPricingRows], ); + const allSampleMeasurementRowsConfirmedByPricing = + productSampleMeasurementRows.length > 0 && + productSampleMeasurementRows.every((row) => + productPricingSystemEnteredSkuIdSet.has(row.skuId), + ); + const displayedSampleMeasurementRowsConfirmedByPricing = + displayedProductSampleMeasurementRows.length > 0 && + displayedProductSampleMeasurementRows.every((row) => + productPricingSystemEnteredSkuIdSet.has(row.skuId), + ); const displayedProductFactoryHasSystemEnteredPricingRows = displayedProductFactoryRows.some((row) => productPricingSystemEnteredSkuIdSet.has(row.skuId), @@ -179,7 +192,10 @@ export function useNonIntegratedWorkflowCompletion({ return { allFinishedSampleShipmentRowsComplete, + allProductFactoryRowsConfirmed, allProductFactoryRowsComplete, + allProductPricingRowsSystemEntered, + allSampleMeasurementRowsConfirmedByPricing, allSampleMeasurementRowsComplete, confirmedProductFactoryRowCount, displayedFinishedSampleShipmentRowsComplete, @@ -192,6 +208,7 @@ export function useNonIntegratedWorkflowCompletion({ displayedProductPricingRowsAllSystemEntered, displayedProductPricingRowsComplete, displayedProductPricingRowsReadyForPush, + displayedSampleMeasurementRowsConfirmedByPricing, displayedSampleMeasurementRowsComplete, isSampleMeasurementSkuLocked, planningLayoutHasContent, diff --git a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmationState.ts b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmationState.ts index db84f226..5b48d2f9 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmationState.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowConfirmationState.ts @@ -25,19 +25,19 @@ import { type UseNonIntegratedWorkflowConfirmationStateParams = { allFinishedSampleShipmentRowsComplete: boolean; - allProductFactoryRowsComplete: boolean; - allSampleMeasurementRowsComplete: boolean; + allProductFactoryRowsConfirmed: boolean; + allSampleMeasurementRowsConfirmedByPricing: boolean; basicInfoHasContent: boolean; displayedFinishedSampleShipmentRowsComplete: boolean; displayedFirstQualityRowsComplete: boolean; displayedProductFactoryIsConfirmed: boolean; displayedProductLimitRowsComplete: boolean; displayedProductSampleMeasurementRows: Array<{ skuId: string }>; + displayedSampleMeasurementRowsConfirmedByPricing: boolean; displayedSampleMeasurementRowsComplete: boolean; documentWorkspaceApprovedAt: NonIntegratedDocumentWorkspaceApprovalMap; documentWorkspaceConfirmations: NonIntegratedDocumentWorkspaceConfirmationMap; documentWorkspaceRevisionSignatures: Record; - isSampleMeasurementSkuLocked: (skuId: string) => boolean; previousDocumentWorkspaceRevisionSignatures: | Record | null @@ -54,19 +54,19 @@ type UseNonIntegratedWorkflowConfirmationStateParams = { export function useNonIntegratedWorkflowConfirmationState({ allFinishedSampleShipmentRowsComplete, - allProductFactoryRowsComplete, - allSampleMeasurementRowsComplete, + allProductFactoryRowsConfirmed, + allSampleMeasurementRowsConfirmedByPricing, basicInfoHasContent, displayedFinishedSampleShipmentRowsComplete, displayedFirstQualityRowsComplete, displayedProductFactoryIsConfirmed, displayedProductLimitRowsComplete, displayedProductSampleMeasurementRows, + displayedSampleMeasurementRowsConfirmedByPricing, displayedSampleMeasurementRowsComplete, documentWorkspaceApprovedAt, documentWorkspaceConfirmations, documentWorkspaceRevisionSignatures, - isSampleMeasurementSkuLocked, previousDocumentWorkspaceRevisionSignatures, productPricingCanBeConfirmed, projectStatus, @@ -92,13 +92,15 @@ export function useNonIntegratedWorkflowConfirmationState({ const isWorkflowNodeConfirmed = useCallback( (category: WorkflowNodeCategory, hasContent: boolean) => { if (category === PRODUCT_FACTORY_WORKSPACE_CATEGORY) { - return hasContent && allProductFactoryRowsComplete; + return hasContent && allProductFactoryRowsConfirmed; } if (category === SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY) { + if (!hasContent || !allProductFactoryRowsConfirmed) return false; + if (allSampleMeasurementRowsConfirmedByPricing) return true; return ( - hasContent && - allProductFactoryRowsComplete && - allSampleMeasurementRowsComplete + documentWorkspaceConfirmations[category] === true && + Boolean(documentWorkspaceApprovedAt[category]) && + !dirtyWorkflowNodeCategories.has(category) ); } if (category === FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY) { @@ -110,12 +112,6 @@ export function useNonIntegratedWorkflowConfirmationState({ ) { return false; } - if ( - category === SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY && - !displayedProductFactoryIsConfirmed - ) { - return false; - } return ( !shouldRequireWorkflowConfirmation(category) || @@ -127,10 +123,9 @@ export function useNonIntegratedWorkflowConfirmationState({ }, [ allFinishedSampleShipmentRowsComplete, - allProductFactoryRowsComplete, - allSampleMeasurementRowsComplete, + allProductFactoryRowsConfirmed, + allSampleMeasurementRowsConfirmedByPricing, dirtyWorkflowNodeCategories, - displayedProductFactoryIsConfirmed, documentWorkspaceApprovedAt, documentWorkspaceConfirmations, productPricingCanBeConfirmed, @@ -142,11 +137,6 @@ export function useNonIntegratedWorkflowConfirmationState({ Boolean(documentWorkspaceApprovedAt[category]), [documentWorkspaceApprovedAt, documentWorkspaceConfirmations], ); - const isSampleMeasurementNodeLocked = - displayedProductSampleMeasurementRows.length > 0 && - displayedProductSampleMeasurementRows.every((row) => - isSampleMeasurementSkuLocked(row.skuId), - ); const isFinishedSampleShipmentNodeLocked = isDocumentWorkspaceNodeLocked( FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY, ); @@ -156,12 +146,20 @@ export function useNonIntegratedWorkflowConfirmationState({ const isProductLimitNodeLocked = isDocumentWorkspaceNodeLocked( PRODUCT_LIMIT_WORKSPACE_CATEGORY, ); - const displayedSampleMeasurementIsConfirmed = isWorkflowNodeConfirmed( - SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY, - displayedSampleMeasurementRowsComplete, - ); + const displayedSampleMeasurementHasContent = + displayedProductSampleMeasurementRows.length > 0; + const displayedSampleMeasurementStoredConfirmed = + documentWorkspaceConfirmations[SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY] === + true && + Boolean(documentWorkspaceApprovedAt[SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]) && + !dirtyWorkflowNodeCategories.has(SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY); + const displayedSampleMeasurementIsConfirmed = + displayedSampleMeasurementHasContent && + displayedProductFactoryIsConfirmed && + (displayedSampleMeasurementRowsConfirmedByPricing || + displayedSampleMeasurementStoredConfirmed); const isSampleMeasurementNodeApproved = - isSampleMeasurementNodeLocked && + displayedSampleMeasurementIsConfirmed && !dirtyWorkflowNodeCategories.has(SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY); const displayedFinishedSampleShipmentIsConfirmed = isWorkflowNodeConfirmed( FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY, diff --git a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowRows.ts b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowRows.ts index d7ca3e06..752e7091 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowRows.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/useNonIntegratedWorkflowRows.ts @@ -128,7 +128,7 @@ export function useNonIntegratedWorkflowRows({ () => new Set( productFactoryRows - .filter((row) => row.confirmed || isProductFactoryRowComplete(row)) + .filter((row) => row.confirmed) .map((row) => row.skuId), ), [productFactoryRows], diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/SampleMeasurementWorkspace.tsx b/furniture-3d-system/src/app/intake/non-integrated/workspaces/SampleMeasurementWorkspace.tsx index 7fe185e4..3dd7821e 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/SampleMeasurementWorkspace.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/SampleMeasurementWorkspace.tsx @@ -15,7 +15,7 @@ type SampleMeasurementWorkspaceProps = { displayedRows: ProductSampleMeasurementDisplayRow[]; inheritedSkuBatchTabs: ReactNode; isReadOnly: boolean; - isProductFactorySkuComplete: (skuId: string) => boolean; + isProductFactorySkuConfirmed: (skuId: string) => boolean; isSampleMeasurementSkuLocked: (skuId: string) => boolean; onUpdateRow: ( skuId: string, @@ -50,7 +50,7 @@ export function SampleMeasurementWorkspace({ displayedRows, inheritedSkuBatchTabs, isReadOnly, - isProductFactorySkuComplete, + isProductFactorySkuConfirmed, isSampleMeasurementSkuLocked, onUpdateRow, }: SampleMeasurementWorkspaceProps) { @@ -64,7 +64,7 @@ export function SampleMeasurementWorkspace({ placeholder={placeholder} readOnly={ isReadOnly || - !isProductFactorySkuComplete(row.skuId) || + !isProductFactorySkuConfirmed(row.skuId) || isSampleMeasurementSkuLocked(row.skuId) } mode="singleLineResizable" diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/basic-info/productFactoryHelpers.ts b/furniture-3d-system/src/app/intake/non-integrated/workspaces/basic-info/productFactoryHelpers.ts index f02d173d..4b09b66a 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/basic-info/productFactoryHelpers.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/basic-info/productFactoryHelpers.ts @@ -11,6 +11,7 @@ import { MODEL_SHARED_PRODUCT_FACTORY_KEYS, PACKAGE_SHARED_PRODUCT_FACTORY_PURCHASING_KEYS, PRODUCT_FACTORY_REQUIRED_FIELDS, + SPU_SHARED_PRODUCT_FACTORY_KEYS, } from "./skuFieldConfig"; import { buildAutoBarcode, shouldKeepCustomBarcode } from "./skuAutoHelpers"; import { SUPPLIER_CODE_BY_NAME } from "./supplierDirectory"; @@ -84,6 +85,8 @@ export function normalizeModelSharedProductFactoryRows( }); return rows.map((row) => { + if (row.confirmed) return row; + const modelKey = getSkuFactoryModelKey(row); if (!modelKey) return row; @@ -101,6 +104,40 @@ export function normalizeModelSharedProductFactoryRows( }); } +export function normalizeSpuSharedProductFactoryRows( + rows: NonIntegratedFactoryRow[], +) { + const sharedValueByKey = new Map(); + SPU_SHARED_PRODUCT_FACTORY_KEYS.forEach((key) => { + const sharedValue = rows.find((row) => row[key].trim())?.[key].trim() ?? ""; + sharedValueByKey.set(key, sharedValue); + }); + + return rows.map((row) => { + if (row.confirmed) return row; + + let nextRow = row; + SPU_SHARED_PRODUCT_FACTORY_KEYS.forEach((key) => { + const sharedValue = sharedValueByKey.get(key); + if (sharedValue === undefined || nextRow[key] === sharedValue) return; + nextRow = { + ...nextRow, + [key]: sharedValue, + }; + }); + + return nextRow; + }); +} + +export function normalizeSharedProductFactoryRows( + rows: NonIntegratedFactoryRow[], +) { + return normalizeSpuSharedProductFactoryRows( + normalizeModelSharedProductFactoryRows(rows), + ); +} + export function getProductFactoryMissingFieldLabels( row: NonIntegratedFactoryRow, ) { @@ -123,7 +160,7 @@ export function getProductFactoryStatusMessage({ if (rows.length === 0) return null; if (isRowsComplete) { return { - message: "产品定厂已完成", + message: "资料已填完核对无误请确认。", type: "success" as const, }; } @@ -249,7 +286,7 @@ export function buildProductFactoryRows( ); }); - return normalizeModelSharedProductFactoryRows(rows); + return normalizeSharedProductFactoryRows(rows); } export function normalizeFactoryRowsForPayload( @@ -294,5 +331,5 @@ export function normalizeFactoryRowsForPayload( }; }); - return normalizeModelSharedProductFactoryRows(normalizedRows); + return normalizeSharedProductFactoryRows(normalizedRows); } diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/basic-info/skuFieldConfig.ts b/furniture-3d-system/src/app/intake/non-integrated/workspaces/basic-info/skuFieldConfig.ts index 68c9272f..8685cdec 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/basic-info/skuFieldConfig.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/basic-info/skuFieldConfig.ts @@ -161,10 +161,13 @@ export const PACKAGE_SHARED_PRODUCT_FACTORY_PURCHASING_KEYS = "assemblyMode", ]); export const MODEL_SHARED_PRODUCT_FACTORY_KEYS = new Set([ + "installationService", +]); +export const SPU_SHARED_PRODUCT_FACTORY_KEYS = new Set([ "warehouse", "shippingMethod", + "installMethod", "productionType", - "installationService", "supplierCategory", "purchaseOrderMethod", ]); diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/documents/documentWorkspaceConfig.ts b/furniture-3d-system/src/app/intake/non-integrated/workspaces/documents/documentWorkspaceConfig.ts index f681d181..55ecb30f 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/documents/documentWorkspaceConfig.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/documents/documentWorkspaceConfig.ts @@ -28,8 +28,6 @@ export const PRODUCT_LIMIT_WORKSPACE_CATEGORY: DocumentWorkspaceCategory = export const PLANNING_LAYOUT_WORKSPACE_CATEGORY: DocumentWorkspaceCategory = NON_INTEGRATED_PLANNING_LAYOUT_WORKSPACE_CATEGORY; export const AUTO_COMPLETED_WORKFLOW_CATEGORIES = new Set([ - PRODUCT_FACTORY_WORKSPACE_CATEGORY, - SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY, FINISHED_SAMPLE_SHIPMENT_WORKSPACE_CATEGORY, ]); export const FACTORY_QUALITY_WORKSPACE_CATEGORY: DocumentWorkspaceCategory = diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactoryHeaderActions.tsx b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactoryHeaderActions.tsx index 8c559917..1cc9132f 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactoryHeaderActions.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactoryHeaderActions.tsx @@ -10,6 +10,7 @@ type ProductFactoryHeaderActionsProps = { isReadOnly: boolean; rowCount: number; hasPendingRows: boolean; + isConfirmed: boolean; hasSystemEnteredPricingRows: boolean; isReopenPickerOpen: boolean; reopenField: ProductFactoryEditableKey; @@ -26,6 +27,7 @@ export function ProductFactoryHeaderActions({ isReadOnly, rowCount, hasPendingRows, + isConfirmed, hasSystemEnteredPricingRows, isReopenPickerOpen, reopenField, @@ -98,10 +100,15 @@ export function ProductFactoryHeaderActions({ ) : null}
); diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactoryInfoTable.tsx b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactoryInfoTable.tsx index d3ad91f2..04cea71a 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactoryInfoTable.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactoryInfoTable.tsx @@ -141,14 +141,18 @@ export function ProductFactoryInfoTable({ - {isProductFactoryRowComplete(row) - ? "已完成" - : "当前资料未填完"} + {row.confirmed + ? "已确认" + : isProductFactoryRowComplete(row) + ? "资料已填完" + : "当前资料未填完"} diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactorySkuInfoTable.tsx b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactorySkuInfoTable.tsx index 5c3371c4..f47aa5f4 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactorySkuInfoTable.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/ProductFactorySkuInfoTable.tsx @@ -40,14 +40,18 @@ export function ProductFactorySkuInfoTable({ - {isProductFactoryRowComplete(row) - ? "已完成" - : "当前资料未填完"} + {row.confirmed + ? "已确认" + : isProductFactoryRowComplete(row) + ? "资料已填完" + : "当前资料未填完"} {row.model || "-"} diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/productFactoryTargetSkuHelpers.ts b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/productFactoryTargetSkuHelpers.ts index bfb2d1cc..43037ec2 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/productFactoryTargetSkuHelpers.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/productFactoryTargetSkuHelpers.ts @@ -6,6 +6,7 @@ import { MODEL_AUTOFILL_PRODUCT_FACTORY_KEYS, MODEL_SHARED_PRODUCT_FACTORY_KEYS, PACKAGE_SHARED_PRODUCT_FACTORY_KEYS, + SPU_SHARED_PRODUCT_FACTORY_KEYS, type ProductFactoryEditableKey, type ProductFactoryModelAutofillEntry, } from "../basic-info/skuFieldConfig"; @@ -48,6 +49,10 @@ export function getProductFactoryTargetSkuIds( const targetItem = items.find((item) => item.id === targetId); if (!targetItem) return new Set([targetId]); + if (SPU_SHARED_PRODUCT_FACTORY_KEYS.has(key)) { + return new Set(items.map((item) => item.id)); + } + if (MODEL_SHARED_PRODUCT_FACTORY_KEYS.has(key)) { const targetModelKey = getSkuFactoryModelKey(targetItem); if (!targetModelKey) return new Set([targetId]); diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/useProductFactoryRowController.ts b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/useProductFactoryRowController.ts index 2bc298c1..b7bb17c0 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/useProductFactoryRowController.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-factory/useProductFactoryRowController.ts @@ -4,6 +4,7 @@ import type { SetStateAction, } from "react"; +import type { PersistEditedProductOverrides } from "../../nonIntegratedDetailTypes"; import type { NonIntegratedDocumentWorkspaceApprovalMap, NonIntegratedDocumentWorkspaceConfirmationMap, @@ -16,10 +17,15 @@ import { type ProductFactoryEditableKey, type ProductFactoryModelAutofillEntry, } from "../basic-info/skuFieldConfig"; -import { normalizeFactorySupplier, getProductFactoryStatusMessage } from "../basic-info/productFactoryHelpers"; +import { + getProductFactoryStatusMessage, + normalizeFactoryRowsForPayload, + normalizeFactorySupplier, +} from "../basic-info/productFactoryHelpers"; import { buildSkuItemsAfterSetCountCommit } from "../basic-info/skuSetCountCommitHelpers"; import { getStyleKeyFromModel } from "../style-assets/styleFileHelpers"; import { PRODUCT_FACTORY_WORKSPACE_CATEGORY } from "../documents/documentWorkspaceConfig"; +import type { WorkflowNodeCategory } from "../documents/documentWorkspaceHelpers"; import { confirmProductFactorySupplierRowsForSku, reopenProductFactoryRows, @@ -35,18 +41,30 @@ type ApplySkuSupplierName = ( value: string, ) => NonIntegratedSkuItem; +type PersistEditedProduct = (params: { + successMessage: string; + errorFallbackMessage: string; + overrides?: PersistEditedProductOverrides; + suppressSuccessMessage?: boolean; +}) => Promise; + type UseProductFactoryRowControllerParams = { applySkuSupplierName: ApplySkuSupplierName; derivedSkuItems: NonIntegratedSkuItem[]; displayedProductFactoryHasPendingRows: boolean; displayedProductFactoryHasSystemEnteredPricingRows: boolean; + displayedProductFactoryIsConfirmed: boolean; displayedProductFactoryRows: NonIntegratedFactoryRow[]; displayedProductFactoryRowsComplete: boolean; documentWorkspaceApprovedAt: NonIntegratedDocumentWorkspaceApprovalMap; documentWorkspaceConfirmations: NonIntegratedDocumentWorkspaceConfirmationMap; + documentWorkspaceRevisionSignatureRef: MutableRefObject< + Record | null + >; isCurrentUserAdmin: boolean; isReadOnly: boolean; isSkuItemFactoryConfirmed: (item: NonIntegratedSkuItem) => boolean; + persistEditedProduct: PersistEditedProduct; productFactoryModelAutofillRef: MutableRefObject< Map >; @@ -73,13 +91,16 @@ export function useProductFactoryRowController({ derivedSkuItems, displayedProductFactoryHasPendingRows, displayedProductFactoryHasSystemEnteredPricingRows, + displayedProductFactoryIsConfirmed, displayedProductFactoryRows, displayedProductFactoryRowsComplete, documentWorkspaceApprovedAt, documentWorkspaceConfirmations, + documentWorkspaceRevisionSignatureRef, isCurrentUserAdmin, isReadOnly, isSkuItemFactoryConfirmed, + persistEditedProduct, productFactoryModelAutofillRef, productFactoryReopenField, productFactoryRows, @@ -156,32 +177,32 @@ export function useProductFactoryRowController({ if (key === "installMethod") { setSkuItems((current) => { - const targetItem = workflowSkuItems.find((item) => item.id === skuId); - if (!targetItem) return current; + const targetSkuIds = getProductFactoryTargetSkuIds( + workflowSkuItems, + skuId, + key, + ); + const targetStyleKeys = new Set( + workflowSkuItems + .filter((item) => targetSkuIds.has(item.id)) + .map((item) => getStyleKeyFromModel(item.model)) + .filter(Boolean), + ); - const targetStyleKey = getStyleKeyFromModel(targetItem.model); - if (targetStyleKey) { - const targetSkuIds = new Set( - workflowSkuItems - .filter((item) => getStyleKeyFromModel(item.model) === targetStyleKey) - .map((item) => item.id), - ); - - setStyleDetails((currentDetails) => - updateStyleDetailEntry(currentDetails, targetStyleKey, { - installMethod: value, - }), - ); - - return current.map((item) => - targetSkuIds.has(item.id) - ? { ...item, installMethod: value } - : item, - ); + if (targetStyleKeys.size > 0) { + setStyleDetails((currentDetails) => { + let nextDetails = currentDetails; + targetStyleKeys.forEach((styleKey) => { + nextDetails = updateStyleDetailEntry(nextDetails, styleKey, { + installMethod: value, + }); + }); + return nextDetails; + }); } return current.map((item) => - item.id === skuId ? { ...item, installMethod: value } : item, + targetSkuIds.has(item.id) ? { ...item, installMethod: value } : item, ); }); } @@ -235,13 +256,65 @@ export function useProductFactoryRowController({ setSaveMessageType("success"); } - function handleProductFactoryStatusClick() { + async function handleProductFactoryStatusClick() { const statusMessage = getProductFactoryStatusMessage({ isRowsComplete: displayedProductFactoryRowsComplete, rows: displayedProductFactoryRows, }); if (!statusMessage) return; + if ( + !isReadOnly && + displayedProductFactoryRowsComplete && + !displayedProductFactoryIsConfirmed + ) { + const targetSkuIds = new Set( + displayedProductFactoryRows.map((row) => row.skuId), + ); + const normalizedRows = normalizeFactoryRowsForPayload( + productFactoryRows, + supplierCodeByName, + ); + const nextRows = normalizedRows.map((row) => + targetSkuIds.has(row.skuId) ? { ...row, confirmed: true } : row, + ); + const allRowsConfirmed = + nextRows.length > 0 && nextRows.every((row) => row.confirmed); + const approvedAt = new Date().toISOString(); + const nextConfirmations = { + ...documentWorkspaceConfirmations, + [PRODUCT_FACTORY_WORKSPACE_CATEGORY]: allRowsConfirmed, + }; + const nextApprovedAt = { ...documentWorkspaceApprovedAt }; + if (allRowsConfirmed) { + nextApprovedAt[PRODUCT_FACTORY_WORKSPACE_CATEGORY] = approvedAt; + } else { + delete nextApprovedAt[PRODUCT_FACTORY_WORKSPACE_CATEGORY]; + } + + setFactoryRows(nextRows); + setDocumentWorkspaceConfirmations(nextConfirmations); + setDocumentWorkspaceApprovedAt(nextApprovedAt); + if (documentWorkspaceRevisionSignatureRef.current) { + documentWorkspaceRevisionSignatureRef.current = { + ...documentWorkspaceRevisionSignatureRef.current, + [PRODUCT_FACTORY_WORKSPACE_CATEGORY]: JSON.stringify(nextRows), + }; + } + await persistEditedProduct({ + successMessage: allRowsConfirmed + ? "产品定厂已确认" + : "当前批次产品定厂已确认", + errorFallbackMessage: "产品定厂确认保存失败", + overrides: { + factoryRows: nextRows, + documentWorkspaceConfirmations: nextConfirmations, + documentWorkspaceApprovedAt: nextApprovedAt, + }, + }); + return; + } + setSaveMessage(statusMessage.message); setSaveMessageType(statusMessage.type); } diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-workflow/useProductWorkflowRowController.ts b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-workflow/useProductWorkflowRowController.ts index 760071d5..44f90cb7 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-workflow/useProductWorkflowRowController.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/product-workflow/useProductWorkflowRowController.ts @@ -20,7 +20,10 @@ import type { NonIntegratedSkuItem, } from "../../nonIntegratedStorage"; import type { FinishedSampleShipmentEditableKey } from "../basic-info/skuFieldConfig"; -import { PRODUCT_PRICING_WORKSPACE_CATEGORY } from "../documents/documentWorkspaceConfig"; +import { + PRODUCT_PRICING_WORKSPACE_CATEGORY, + SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY, +} from "../documents/documentWorkspaceConfig"; import type { WorkflowNodeCategory } from "../documents/documentWorkspaceHelpers"; import { updateFirstQualityRowsForSku, @@ -77,7 +80,7 @@ type UseProductWorkflowRowControllerParams = { documentWorkspaceRevisionSignatureRef: MutableRefObject< Record | null >; - isProductFactorySkuComplete: (skuId: string) => boolean; + isProductFactorySkuConfirmed: (skuId: string) => boolean; isProductLimitNodeLocked: boolean; isProductPricingNodeLocked: boolean; isReadOnly: boolean; @@ -89,6 +92,8 @@ type UseProductWorkflowRowControllerParams = { planningLayoutRowsRef: MutableRefObject; pricingRows: NonIntegratedPricingRow[]; productId?: string; + productSampleMeasurementRows: NonIntegratedSampleMeasurementRow[]; + sampleMeasurementRows: NonIntegratedSampleMeasurementRow[]; scheduleAutoSave: (overrides?: PersistEditedProductOverrides) => void; setDocumentWorkspaceApprovedAt: Dispatch< SetStateAction @@ -128,7 +133,7 @@ export function useProductWorkflowRowController({ documentWorkspaceApprovedAt, documentWorkspaceConfirmations, documentWorkspaceRevisionSignatureRef, - isProductFactorySkuComplete, + isProductFactorySkuConfirmed, isProductLimitNodeLocked, isProductPricingNodeLocked, isReadOnly, @@ -140,6 +145,8 @@ export function useProductWorkflowRowController({ planningLayoutRowsRef, pricingRows, productId, + productSampleMeasurementRows, + sampleMeasurementRows, scheduleAutoSave, setDocumentWorkspaceApprovedAt, setDocumentWorkspaceConfirmations, @@ -233,16 +240,25 @@ export function useProductWorkflowRowController({ }); const nextProductPricingCanBeConfirmed = canConfirmProductPricingRows(nextPricingRows); + const nextSampleMeasurementCanBeConfirmedByPricing = + nextProductPricingCanBeConfirmed && + productSampleMeasurementRows.length > 0; const nextApprovedAt = nextProductPricingCanBeConfirmed ? { ...documentWorkspaceApprovedAt, [PRODUCT_PRICING_WORKSPACE_CATEGORY]: pushedAt, + ...(nextSampleMeasurementCanBeConfirmedByPricing + ? { [SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: pushedAt } + : {}), } : documentWorkspaceApprovedAt; const nextConfirmations = nextProductPricingCanBeConfirmed ? { ...documentWorkspaceConfirmations, [PRODUCT_PRICING_WORKSPACE_CATEGORY]: true, + ...(nextSampleMeasurementCanBeConfirmedByPricing + ? { [SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: true } + : {}), } : documentWorkspaceConfirmations; const pushSuccessMessage = @@ -257,6 +273,12 @@ export function useProductWorkflowRowController({ ...documentWorkspaceRevisionSignatureRef.current, [PRODUCT_PRICING_WORKSPACE_CATEGORY]: JSON.stringify(nextPricingRows), + ...(nextSampleMeasurementCanBeConfirmedByPricing + ? { + [SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: + JSON.stringify(sampleMeasurementRows), + } + : {}), }; } } @@ -456,7 +478,7 @@ export function useProductWorkflowRowController({ ) { if ( isReadOnly || - !isProductFactorySkuComplete(skuId) || + !isProductFactorySkuConfirmed(skuId) || isSampleMeasurementSkuLocked(skuId) ) return; diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/SampleMeasurementHeaderActions.tsx b/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/SampleMeasurementHeaderActions.tsx index 039f526e..689eddf0 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/SampleMeasurementHeaderActions.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/SampleMeasurementHeaderActions.tsx @@ -4,6 +4,7 @@ type SampleMeasurementHeaderActionsProps = { actionGroupClassName: string; rowCount: number; isProductFactoryConfirmed: boolean; + hasPendingRows: boolean; isSampleMeasurementConfirmed: boolean; onStatusClick: () => void; }; @@ -12,6 +13,7 @@ export function SampleMeasurementHeaderActions({ actionGroupClassName, rowCount, isProductFactoryConfirmed, + hasPendingRows, isSampleMeasurementConfirmed, onStatusClick, }: SampleMeasurementHeaderActionsProps) { @@ -19,6 +21,7 @@ export function SampleMeasurementHeaderActions({
); diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/sampleWorkflowHelpers.ts b/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/sampleWorkflowHelpers.ts index 64e384a3..9a635cda 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/sampleWorkflowHelpers.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/sampleWorkflowHelpers.ts @@ -153,7 +153,7 @@ export function getSampleMeasurementStatusMessage({ } return { - message: "样包测量已完成", + message: "资料已填完核对无误请确认。", type: "success" as const, }; } diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/useSampleWorkflowStatusController.ts b/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/useSampleWorkflowStatusController.ts index f77eb1de..4a2cb5b1 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/useSampleWorkflowStatusController.ts +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/sample-workflow/useSampleWorkflowStatusController.ts @@ -1,37 +1,77 @@ import type { Dispatch, + MutableRefObject, SetStateAction, } from "react"; +import type { PersistEditedProductOverrides } from "../../nonIntegratedDetailTypes"; +import type { + NonIntegratedDocumentWorkspaceApprovalMap, + NonIntegratedDocumentWorkspaceConfirmationMap, +} from "../../nonIntegratedStorage"; +import { SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY } from "../documents/documentWorkspaceConfig"; +import type { WorkflowNodeCategory } from "../documents/documentWorkspaceHelpers"; import { getFinishedSampleShipmentStatusMessage, getSampleMeasurementStatusMessage, + normalizeSampleMeasurementRowsForPayload, type ProductFinishedSampleShipmentDisplayRow, type ProductSampleMeasurementDisplayRow, } from "./sampleWorkflowHelpers"; type SaveMessageType = "success" | "error"; +type PersistEditedProduct = (params: { + successMessage: string; + errorFallbackMessage: string; + overrides?: PersistEditedProductOverrides; + suppressSuccessMessage?: boolean; +}) => Promise; + type UseSampleWorkflowStatusControllerParams = { + documentWorkspaceApprovedAt: NonIntegratedDocumentWorkspaceApprovalMap; + documentWorkspaceConfirmations: NonIntegratedDocumentWorkspaceConfirmationMap; displayedFinishedSampleShipmentRows: ProductFinishedSampleShipmentDisplayRow[]; displayedFinishedSampleShipmentRowsComplete: boolean; displayedProductFactoryIsConfirmed: boolean; displayedProductSampleMeasurementRows: ProductSampleMeasurementDisplayRow[]; + displayedSampleMeasurementIsConfirmed: boolean; displayedSampleMeasurementRowsComplete: boolean; + documentWorkspaceRevisionSignatureRef: MutableRefObject< + Record | null + >; + isReadOnly: boolean; + persistEditedProduct: PersistEditedProduct; + productSampleMeasurementRows: ProductSampleMeasurementDisplayRow[]; + setDocumentWorkspaceApprovedAt: Dispatch< + SetStateAction + >; + setDocumentWorkspaceConfirmations: Dispatch< + SetStateAction + >; setSaveMessage: Dispatch>; setSaveMessageType: Dispatch>; }; export function useSampleWorkflowStatusController({ + documentWorkspaceApprovedAt, + documentWorkspaceConfirmations, displayedFinishedSampleShipmentRows, displayedFinishedSampleShipmentRowsComplete, displayedProductFactoryIsConfirmed, displayedProductSampleMeasurementRows, + displayedSampleMeasurementIsConfirmed, displayedSampleMeasurementRowsComplete, + documentWorkspaceRevisionSignatureRef, + isReadOnly, + persistEditedProduct, + productSampleMeasurementRows, + setDocumentWorkspaceApprovedAt, + setDocumentWorkspaceConfirmations, setSaveMessage, setSaveMessageType, }: UseSampleWorkflowStatusControllerParams) { - function handleSampleMeasurementStatusClick() { + async function handleSampleMeasurementStatusClick() { const statusMessage = getSampleMeasurementStatusMessage({ isFactoryConfirmed: displayedProductFactoryIsConfirmed, isRowsComplete: displayedSampleMeasurementRowsComplete, @@ -39,6 +79,47 @@ export function useSampleWorkflowStatusController({ }); if (!statusMessage) return; + if ( + !isReadOnly && + displayedProductFactoryIsConfirmed && + displayedSampleMeasurementRowsComplete && + !displayedSampleMeasurementIsConfirmed + ) { + const approvedAt = new Date().toISOString(); + const nextConfirmations = { + ...documentWorkspaceConfirmations, + [SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: true, + }; + const nextApprovedAt = { + ...documentWorkspaceApprovedAt, + [SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: approvedAt, + }; + const nextSampleMeasurementRows = normalizeSampleMeasurementRowsForPayload( + productSampleMeasurementRows, + ); + + setDocumentWorkspaceConfirmations(nextConfirmations); + setDocumentWorkspaceApprovedAt(nextApprovedAt); + if (documentWorkspaceRevisionSignatureRef.current) { + documentWorkspaceRevisionSignatureRef.current = { + ...documentWorkspaceRevisionSignatureRef.current, + [SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY]: JSON.stringify( + nextSampleMeasurementRows, + ), + }; + } + await persistEditedProduct({ + successMessage: "样包测量已确认", + errorFallbackMessage: "样包测量确认保存失败", + overrides: { + sampleMeasurementRows: nextSampleMeasurementRows, + documentWorkspaceConfirmations: nextConfirmations, + documentWorkspaceApprovedAt: nextApprovedAt, + }, + }); + return; + } + setSaveMessage(statusMessage.message); setSaveMessageType(statusMessage.type); } diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/shared/DocumentWorkspaceHeaderActionsSlot.tsx b/furniture-3d-system/src/app/intake/non-integrated/workspaces/shared/DocumentWorkspaceHeaderActionsSlot.tsx index 7ee99435..f9e1e4ac 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/shared/DocumentWorkspaceHeaderActionsSlot.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/shared/DocumentWorkspaceHeaderActionsSlot.tsx @@ -54,6 +54,7 @@ export function DocumentWorkspaceHeaderActionsSlot({ displayedProductPricingRowsComplete, displayedProductPricingRowsReadyForPush, displayedProductSampleMeasurementRowCount, + displayedSampleMeasurementHasPendingRows, displayedSampleMeasurementIsConfirmed, documentWorkspaceConfirmationAction, documentWorkspaceUploadInputRef, @@ -157,6 +158,7 @@ export function DocumentWorkspaceHeaderActionsSlot({ displayedProductPricingRowsComplete: boolean; displayedProductPricingRowsReadyForPush: boolean; displayedProductSampleMeasurementRowCount: number; + displayedSampleMeasurementHasPendingRows: boolean; displayedSampleMeasurementIsConfirmed: boolean; documentWorkspaceConfirmationAction: ReactNode; documentWorkspaceUploadInputRef: RefObject; @@ -262,6 +264,7 @@ export function DocumentWorkspaceHeaderActionsSlot({ isReadOnly={isReadOnly} rowCount={displayedProductFactoryRowCount} hasPendingRows={displayedProductFactoryHasPendingRows} + isConfirmed={displayedProductFactoryIsConfirmed} hasSystemEnteredPricingRows={ displayedProductFactoryHasSystemEnteredPricingRows } @@ -282,6 +285,7 @@ export function DocumentWorkspaceHeaderActionsSlot({ actionGroupClassName={actionGroupClassName} rowCount={displayedProductSampleMeasurementRowCount} isProductFactoryConfirmed={displayedProductFactoryIsConfirmed} + hasPendingRows={displayedSampleMeasurementHasPendingRows} isSampleMeasurementConfirmed={displayedSampleMeasurementIsConfirmed} onStatusClick={onSampleMeasurementStatusClick} /> diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/shared/DocumentWorkspaceSidebar.tsx b/furniture-3d-system/src/app/intake/non-integrated/workspaces/shared/DocumentWorkspaceSidebar.tsx index e953b106..ce93db11 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/shared/DocumentWorkspaceSidebar.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/shared/DocumentWorkspaceSidebar.tsx @@ -10,8 +10,10 @@ import { import { DocumentWorkspaceSidebarStatusIcon } from "../../components/DocumentWorkspaceControls"; import type { NonIntegratedFactoryRow, + NonIntegratedSampleMeasurementRow, NonIntegratedUploadFile, } from "../../nonIntegratedStorage"; +import { SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY } from "../documents/documentWorkspaceConfig"; type DocumentWorkspaceView = "detail" | "document"; @@ -27,9 +29,10 @@ export function DocumentWorkspaceSidebar({ basicInfoNeedsConfirmation, documentWorkspaceCountByCategory, documentWorkspaceView, - isProductFactoryRowComplete, isWorkflowNodeConfirmed, productFactoryRows, + sampleMeasurementRows, + isSampleMeasurementConfirmed, visibleGroupedUploadFiles, onOpenBasicInfo, onOpenDocumentWorkspace, @@ -42,12 +45,13 @@ export function DocumentWorkspaceSidebar({ Record >; documentWorkspaceView: DocumentWorkspaceView; - isProductFactoryRowComplete: (row: NonIntegratedFactoryRow) => boolean; isWorkflowNodeConfirmed: ( category: DocumentWorkspaceCategory, hasContent: boolean, ) => boolean; productFactoryRows: NonIntegratedFactoryRow[]; + sampleMeasurementRows: NonIntegratedSampleMeasurementRow[]; + isSampleMeasurementConfirmed: boolean; visibleGroupedUploadFiles: GroupedWorkspaceFiles[]; onOpenBasicInfo: () => void; onOpenDocumentWorkspace: (category: DocumentWorkspaceCategory) => void; @@ -74,13 +78,14 @@ export function DocumentWorkspaceSidebar({ const categoryHasContent = category === PRODUCT_FACTORY_WORKSPACE_CATEGORY ? productFactoryRows.length > 0 + : category === SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY + ? sampleMeasurementRows.length > 0 : hasFiles; const categoryNeedsConfirmation = category === PRODUCT_FACTORY_WORKSPACE_CATEGORY - ? !( - productFactoryRows.length > 0 && - productFactoryRows.every(isProductFactoryRowComplete) - ) + ? !productFactoryRows.every((row) => row.confirmed) + : category === SAMPLE_MEASUREMENT_WORKSPACE_CATEGORY + ? !isSampleMeasurementConfirmed : needsConfirmation; const shouldInsertBasicInfoTrigger = index === basicInfoInsertIndex; const basicInfoIsActive = documentWorkspaceView === "detail"; diff --git a/furniture-3d-system/src/app/mobile/HomeMobileView.tsx b/furniture-3d-system/src/app/mobile/HomeMobileView.tsx index 19ced019..7d85a424 100644 --- a/furniture-3d-system/src/app/mobile/HomeMobileView.tsx +++ b/furniture-3d-system/src/app/mobile/HomeMobileView.tsx @@ -8,7 +8,7 @@ import Input, { Textarea } from '@/components/ui/Input' import Modal from '@/components/ui/Modal' import Button from '@/components/ui/Button' import Select from '@/components/ui/Select' -import { ToastContainer } from '@/components/ui/Toast' +import { ToastContainer, type ToastData } from '@/components/ui/Toast' import type { ProductWithSkus } from '../home/home-client.types' import type { LucideIcon } from 'lucide-react' @@ -63,12 +63,12 @@ export interface HomeMobileViewProps { /* 删除产品 */ deletingProduct: ProductWithSkus | null - onDeleteProduct: () => void + onDeleteProduct: () => void | Promise onCloseDeleteProduct: () => void /* 删除 SKU */ deletingSku: { product: ProductWithSkus; sku: { skuCode: string } } | null - onDeleteSKU: () => void + onDeleteSKU: () => void | Promise onCloseDeleteSKU: () => void /* 添加 SKU */ @@ -81,7 +81,7 @@ export interface HomeMobileViewProps { onMarkHomeReturn: () => void /* Toast */ - toasts: Array<{ id: string; message: string; type?: string }> + toasts: ToastData[] onRemoveToast: (id: string) => void } @@ -406,7 +406,9 @@ export default function HomeMobileView({ { + await onDeleteProduct() + }} title="删除产品" description={ deletingProduct @@ -428,7 +430,9 @@ export default function HomeMobileView({ { + await onDeleteSKU() + }} title="删除 SKU" description={ deletingSku