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
@@ -1,7 +1,10 @@
"use client"

import Link from "next/link"
import { ArrowLeft, Package } from "lucide-react"
import { useState } from "react"
import { ArrowLeft, Download, Loader2, Package } from "lucide-react"
import { useRouter } from "next/navigation"
import { toast } from "sonner"

import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
Expand All @@ -15,13 +18,34 @@ import { ProductRoutingTab } from "@/components/finance/cost-product-master/rout
import { ProductAuditTab } from "@/components/finance/cost-product-master/audit-tab"
import { CostHistoryTab } from "@/components/finance/cost-results/cost-history-tab"
import { ProductTypeName } from "@/components/common/product-type-name"
import { exportBulkProductRouting } from "@/services/finance/cost-import-api"

interface Props {
productSysId: number
}

export default function ProductMasterDetailClient({ productSysId }: Props) {
const { data: product, isLoading } = useCostProductMaster(productSysId)
const router = useRouter()
const [exporting, setExporting] = useState(false)

async function handleExport() {
setExporting(true)
try {
const result = await exportBulkProductRouting({ productSysIds: [productSysId] })
toast.success(`Export dijadwalkan — Job #${result.jobId}`, {
description: "Termasuk semua intermediate product yang berkaitan.",
action: {
label: "Lihat job",
onClick: () => router.push("/finance/import-jobs"),
},
})
} catch {
toast.error("Export gagal, coba lagi.")
} finally {
setExporting(false)
}
}

return (
<div className="space-y-6">
Expand All @@ -47,7 +71,19 @@ export default function ProductMasterDetailClient({ productSysId }: Props) {
: undefined
}
/>
{product && <CalculateButton productSysId={productSysId} label="Calculate cost" />}
<div className="flex flex-wrap gap-2">
{product && <CalculateButton productSysId={productSysId} label="Calculate cost" />}
{product && (
<Button variant="outline" size="sm" onClick={handleExport} disabled={exporting}>
{exporting ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<Download className="mr-2 h-4 w-4" />
)}
{exporting ? "Memproses…" : "Export product + routing"}
</Button>
)}
</div>
</div>

{product && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ export default function ProductMasterPageClient() {
async function handleBulkExport() {
setBulkExportLoading(true)
try {
const result = await exportBulkProductRouting()
const isFiltered = !!filters.search || !!filters.productTypeId
const visibleItems = data?.items ?? []
const productSysIds = isFiltered ? visibleItems.map((p) => p.productSysId) : undefined
const result = await exportBulkProductRouting({ productSysIds })
toast.success(`Export dijadwalkan — Job #${result.jobId}`, {
description: "File akan tersedia di halaman Import Jobs setelah selesai diproses.",
action: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ export async function POST(request: NextRequest) {
const productTypeCodes: string[] = Array.isArray(body.productTypeCodes)
? (body.productTypeCodes as string[])
: []
const productSysIds: number[] = Array.isArray(body.productSysIds)
? (body.productSysIds as unknown[]).map(Number)
: []

const includeRouting: boolean =
typeof body.includeRouting === "boolean" ? body.includeRouting : true
const activeOnly: boolean =
typeof body.activeOnly === "boolean" ? body.activeOnly : false

const res = await getCostDataImportClient().exportBulkProductRouting(
{ productTypeCodes, includeRouting, activeOnly },
{ productTypeCodes, productSysIds, includeRouting, activeOnly },
metadata,
)

Expand Down
10 changes: 8 additions & 2 deletions src/services/finance/cost-import-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,16 +227,22 @@ export async function validateBulkProductRoutingFile(
}

/**
* Queue an async export of all product master + routing data to MinIO.
* Queue an async export of product master + routing data to MinIO.
* When productSysIds is provided, exports only those products and their full
* transitive dependency closure (intermediates reachable via PRODUCT-type RMs).
* Returns a job ID that can be polled or viewed on the import-jobs page.
*/
export async function exportBulkProductRouting(options?: {
productTypeCodes?: string[]
productSysIds?: number[]
}): Promise<{ jobId: number; status: string }> {
const res = await fetch(`${BASE}/export/bulk_product_routing`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ productTypeCodes: options?.productTypeCodes ?? [] }),
body: JSON.stringify({
productTypeCodes: options?.productTypeCodes ?? [],
productSysIds: options?.productSysIds ?? [],
}),
})
if (!res.ok) throw new Error(`Bulk export failed: ${res.status}`)
const json = await res.json()
Expand Down
41 changes: 40 additions & 1 deletion src/types/generated/finance/v1/cost_import.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading