Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
ProductMasterTable,
} from "@/components/finance/cost-product-master"
import { BulkImportDialog } from "@/components/finance/costing/bulk-import-dialog"
import { ParamsOnlyImportDialog } from "@/components/finance/costing/params-only-import-dialog"
import { ImportDialog } from "@/components/finance/costing/import-dialog"
import { ProductTypeCombobox } from "@/components/finance/comboboxes"
import { DataTablePagination } from "@/components/shared"
Expand Down Expand Up @@ -55,6 +54,7 @@ export default function ProductMasterPageClient() {
const [importOpen, setImportOpen] = useState(false)
const [bulkImportOpen, setBulkImportOpen] = useState(false)
const [paramsImportOpen, setParamsImportOpen] = useState(false)
// Both bulk menu items open the same unified ETL dialog, differing only by kind.
const [editing, setEditing] = useState<CostProductMaster | null>(null)
const [bulkExportLoading, setBulkExportLoading] = useState(false)

Expand Down Expand Up @@ -232,8 +232,8 @@ export default function ProductMasterPageClient() {
onOpenChange={setDeactivateOpen}
product={editing}
/>
<BulkImportDialog open={bulkImportOpen} onOpenChange={setBulkImportOpen} />
<ParamsOnlyImportDialog open={paramsImportOpen} onOpenChange={setParamsImportOpen} />
<BulkImportDialog open={bulkImportOpen} onOpenChange={setBulkImportOpen} kind="product_routing" />
<BulkImportDialog open={paramsImportOpen} onOpenChange={setParamsImportOpen} kind="params_only" />
</div>
)
}
73 changes: 0 additions & 73 deletions src/app/api/v1/finance/costing/import/bulk_params_only/route.ts

This file was deleted.

This file was deleted.

59 changes: 59 additions & 0 deletions src/app/api/v1/finance/costing/import/start/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// POST /api/v1/finance/costing/import/start
// Starts an async ETL import job for an already-uploaded object (from the
// presigned upload). Accepts JSON body:
// { kind: "product_routing" | "params_only", objectKey: string, fileName?: string }

import { NextRequest, NextResponse } from "next/server"
import {
getCostDataImportClient,
createMetadataFromRequest,
isGrpcError,
handleGrpcError,
} from "@/lib/grpc"
import { ImportKind } from "@/types/generated/finance/v1/cost_import"

function toImportKind(kind: string): ImportKind {
if (kind === "product_routing") return ImportKind.IMPORT_KIND_PRODUCT_ROUTING
if (kind === "params_only") return ImportKind.IMPORT_KIND_PARAMS_ONLY
return ImportKind.IMPORT_KIND_UNSPECIFIED
}

export async function POST(request: NextRequest) {
try {
const body = await request.json()
const metadata = createMetadataFromRequest(request)

const res = await getCostDataImportClient().startCostingImport(
{
kind: toImportKind(body.kind),
objectKey: body.objectKey ?? "",
fileName: body.fileName ?? "",
duplicateAction: body.duplicateAction ?? "",
},
metadata,
)

if (res.base && !res.base.isSuccess) {
return NextResponse.json({ base: res.base }, { status: 422 })
}

return NextResponse.json({
base: res.base,
data: { jobId: res.jobId, status: res.status },
})
} catch (error) {
if (isGrpcError(error)) return handleGrpcError(error)
console.error("Error starting costing import:", error)
return NextResponse.json(
{
base: {
isSuccess: false,
statusCode: "500",
message: "Failed to start import",
validationErrors: [],
},
},
{ status: 500 },
)
}
}
56 changes: 56 additions & 0 deletions src/app/api/v1/finance/costing/import/upload-url/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// POST /api/v1/finance/costing/import/upload-url
// Returns a presigned PUT URL so the browser can upload a bulk-import file
// (xlsx or zipped CSV) directly to object storage, bypassing the BFF/gRPC path.
// Accepts JSON body: { kind: "product_routing" | "params_only", fileName: string }

import { NextRequest, NextResponse } from "next/server"
import {
getCostDataImportClient,
createMetadataFromRequest,
isGrpcError,
handleGrpcError,
} from "@/lib/grpc"
import { ImportKind } from "@/types/generated/finance/v1/cost_import"

function toImportKind(kind: string): ImportKind {
if (kind === "product_routing") return ImportKind.IMPORT_KIND_PRODUCT_ROUTING
if (kind === "params_only") return ImportKind.IMPORT_KIND_PARAMS_ONLY
return ImportKind.IMPORT_KIND_UNSPECIFIED
}

export async function POST(request: NextRequest) {
try {
const body = await request.json()
const metadata = createMetadataFromRequest(request)

const res = await getCostDataImportClient().getImportUploadURL(
{ kind: toImportKind(body.kind), fileName: body.fileName ?? "" },
metadata,
)

if (res.base && !res.base.isSuccess) {
return NextResponse.json({ base: res.base }, { status: 422 })
}

return NextResponse.json({
base: res.base,
uploadUrl: res.uploadUrl,
objectKey: res.objectKey,
expiresInSeconds: res.expiresInSeconds,
})
} catch (error) {
if (isGrpcError(error)) return handleGrpcError(error)
console.error("Error getting import upload URL:", error)
return NextResponse.json(
{
base: {
isSuccess: false,
statusCode: "500",
message: "Failed to get upload URL",
validationErrors: [],
},
},
{ status: 500 },
)
}
}

This file was deleted.

Loading
Loading