refactor(intake): extract product pricing workspace
This commit is contained in:
parent
3799702d22
commit
1dca970937
@ -44,6 +44,7 @@ import { DocumentAttachmentWorkspace } from "./DocumentAttachmentWorkspace";
|
||||
import { OpeningReferenceWorkspace } from "./OpeningReferenceWorkspace";
|
||||
import { PatentApplicationWorkspace } from "./PatentApplicationWorkspace";
|
||||
import { PlanningLayoutWorkspace } from "./PlanningLayoutWorkspace";
|
||||
import { ProductPricingWorkspace } from "./ProductPricingWorkspace";
|
||||
import { ProjectReviewWorkspace } from "./ProjectReviewWorkspace";
|
||||
import { ProductDataWorkspace } from "./ProductDataWorkspace";
|
||||
import { QualityInspectionWorkspace } from "./QualityInspectionWorkspace";
|
||||
@ -18596,241 +18597,19 @@ export default function NonIntegratedDetailClient({
|
||||
}
|
||||
|
||||
function renderProductPricingWorkspace() {
|
||||
const renderReadOnlyCell = (value: string) => (
|
||||
<div className="min-w-[112px] rounded border border-apple-separator bg-apple-bg-secondary px-2 py-1.5 text-xs text-apple-label-primary">
|
||||
{value || "-"}
|
||||
</div>
|
||||
);
|
||||
const getVirtualInventoryStatusMeta = (row: ProductPricingDisplayRow) => {
|
||||
const virtualInventorySkipTitle =
|
||||
row.jstVirtualInventoryError || "无需推虚拟库存";
|
||||
const normalizedVirtualInventoryError =
|
||||
row.jstVirtualInventoryError
|
||||
?.replace(/[\u2010-\u2015\u2212\uFE58\uFE63\uFF0D]/g, "-")
|
||||
.replace(/\s+/g, "") ?? "";
|
||||
const isVirtualInventorySkipReason =
|
||||
normalizedVirtualInventoryError.includes("未包含成品代发仓-虚拟库存") ||
|
||||
normalizedVirtualInventoryError.includes("未命中成品代发仓-虚拟库存") ||
|
||||
normalizedVirtualInventoryError.includes("真实库存") ||
|
||||
normalizedVirtualInventoryError.includes("无需推虚拟库存");
|
||||
const skippedMeta = {
|
||||
label: "无需库存",
|
||||
tone: "bg-apple-bg-secondary text-apple-label-secondary",
|
||||
title: virtualInventorySkipTitle,
|
||||
};
|
||||
|
||||
switch (row.jstVirtualInventoryStatus) {
|
||||
case "pending":
|
||||
return {
|
||||
label: "库存待推",
|
||||
tone: "bg-[#e7f1ff] text-[#1764ad]",
|
||||
title: row.jstVirtualInventoryScheduledAt
|
||||
? `计划 ${row.jstVirtualInventoryScheduledAt} 推送`
|
||||
: "已生成虚拟库存待推送",
|
||||
};
|
||||
case "pushed":
|
||||
return {
|
||||
label: "库存已推",
|
||||
tone: "bg-[#dff7e8] text-[#137a38]",
|
||||
title: row.jstVirtualInventoryPushedAt
|
||||
? `已于 ${row.jstVirtualInventoryPushedAt} 推送`
|
||||
: "虚拟库存已推送",
|
||||
};
|
||||
case "failed":
|
||||
if (isVirtualInventorySkipReason) return skippedMeta;
|
||||
return {
|
||||
label: "库存失败",
|
||||
tone: "bg-[#ffe3df] text-[#b42318]",
|
||||
title: row.jstVirtualInventoryError || "虚拟库存推送失败",
|
||||
};
|
||||
case "skipped":
|
||||
return skippedMeta;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const adjustCostPrice = (row: ProductPricingDisplayRow, offset: number) => {
|
||||
if (isReadOnly || isProductPricingNodeLocked) return;
|
||||
const currentValue = Number(row.costPrice);
|
||||
const nextValue = Math.max(
|
||||
0,
|
||||
(Number.isFinite(currentValue) ? currentValue : 0) + offset,
|
||||
);
|
||||
updateProductPricingRow(row.skuId, "costPrice", String(nextValue));
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="product-pricing-workspace"
|
||||
className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-apple-xl border border-apple-separator bg-apple-bg-primary"
|
||||
>
|
||||
<div className="border-b border-apple-separator px-3 py-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{renderInheritedSkuBatchTabs()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="min-h-0 min-w-0 flex-1 overflow-hidden p-3">
|
||||
{productPricingRows.length === 0 ? (
|
||||
<div className="flex min-h-[260px] items-center justify-center rounded-apple-xl border border-dashed border-apple-separator text-sm text-apple-label-secondary">
|
||||
当前还没有 SKU 信息,先维护 SKU 后再核价。
|
||||
</div>
|
||||
) : displayedProductPricingRows.length === 0 ? (
|
||||
<div className="flex min-h-[260px] items-center justify-center rounded-apple-xl border border-dashed border-apple-separator text-sm text-apple-label-secondary">
|
||||
当前批次还没有 SKU 信息。
|
||||
</div>
|
||||
) : (
|
||||
<div className="non-integrated-table-scroll non-integrated-workspace-table-scroll h-full max-w-full rounded-apple-lg border border-apple-separator">
|
||||
<table className="min-w-[1280px] w-full border-collapse text-xs">
|
||||
<thead className="bg-apple-bg-secondary/40 text-apple-label-secondary">
|
||||
<tr>
|
||||
{[
|
||||
"型号",
|
||||
"货号",
|
||||
"条码",
|
||||
"套件量",
|
||||
"规格名称",
|
||||
"供应商名称",
|
||||
"成本价",
|
||||
"是否录入系统",
|
||||
].map((label) => (
|
||||
<th
|
||||
key={label}
|
||||
className="border-b border-apple-separator px-3 py-2 text-left font-apple-medium"
|
||||
>
|
||||
{label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{displayedProductPricingRows.map((row) => {
|
||||
const isSystemEntered =
|
||||
isProductPricingRowSystemEntered(row);
|
||||
const pricingReadyForFuturePush =
|
||||
row.costPrice.trim().length > 0;
|
||||
const virtualInventoryStatusMeta =
|
||||
getVirtualInventoryStatusMeta(row);
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={row.skuId}
|
||||
className="border-b border-apple-separator last:border-b-0"
|
||||
>
|
||||
<td className="px-3 py-2">
|
||||
{renderReadOnlyCell(row.model)}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
{renderReadOnlyCell(row.skuCode)}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
{renderReadOnlyCell(row.barcode)}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
{renderReadOnlyCell(row.setCount)}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
{renderReadOnlyCell(row.specName)}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
{renderReadOnlyCell(row.supplierName)}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<div className="flex min-w-[136px] overflow-hidden rounded border border-apple-separator bg-apple-bg-secondary text-xs text-apple-label-primary">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => adjustCostPrice(row, -1)}
|
||||
disabled={isReadOnly || isProductPricingNodeLocked}
|
||||
className="h-8 w-9 border-r border-apple-separator text-apple-label-secondary transition hover:bg-apple-bg-primary disabled:opacity-40"
|
||||
aria-label="减少成本价"
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<input
|
||||
value={row.costPrice}
|
||||
onChange={(event) =>
|
||||
updateProductPricingRow(
|
||||
row.skuId,
|
||||
"costPrice",
|
||||
event.target.value,
|
||||
)
|
||||
}
|
||||
readOnly={isReadOnly || isProductPricingNodeLocked}
|
||||
placeholder="0"
|
||||
className="h-8 w-16 bg-transparent px-2 text-center outline-none placeholder:text-apple-label-tertiary"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => adjustCostPrice(row, 1)}
|
||||
disabled={isReadOnly || isProductPricingNodeLocked}
|
||||
className="h-8 w-9 border-l border-apple-separator text-apple-label-secondary transition hover:bg-apple-bg-primary disabled:opacity-40"
|
||||
aria-label="增加成本价"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<div className="flex min-w-[236px] items-center gap-2">
|
||||
<span
|
||||
className={`inline-flex h-6 min-w-[52px] items-center justify-center rounded-full px-2 text-[11px] font-apple-medium ${
|
||||
isSystemEntered
|
||||
? "bg-[#dff7e8] text-[#137a38]"
|
||||
: "bg-[#fff3df] text-[#9b6511]"
|
||||
}`}
|
||||
>
|
||||
{isSystemEntered ? "已录入" : "未录入"}
|
||||
</span>
|
||||
{virtualInventoryStatusMeta ? (
|
||||
<span
|
||||
className={`inline-flex h-6 min-w-[64px] items-center justify-center rounded-full px-2 text-[11px] font-apple-medium ${virtualInventoryStatusMeta.tone}`}
|
||||
title={virtualInventoryStatusMeta.title}
|
||||
>
|
||||
{virtualInventoryStatusMeta.label}
|
||||
</span>
|
||||
) : null}
|
||||
{canUseProductPricingPushControls ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
void handleConfirmProductPricingPushRows([row])
|
||||
}
|
||||
disabled={
|
||||
isReadOnly ||
|
||||
isProductPricingNodeLocked ||
|
||||
isSystemEntered ||
|
||||
!pricingReadyForFuturePush ||
|
||||
!PRODUCT_PRICING_JST_PUSH_ENABLED
|
||||
}
|
||||
title={
|
||||
isSystemEntered
|
||||
? "当前 SKU 已录入系统,后续以聚水潭回传状态为准"
|
||||
: PRODUCT_PRICING_JST_PUSH_ENABLED &&
|
||||
pricingReadyForFuturePush
|
||||
? "确认推送当前 SKU 到聚水潭"
|
||||
: pricingReadyForFuturePush
|
||||
? "聚水潭接口未接入,暂不可确认推送"
|
||||
: "请先填写成本价"
|
||||
}
|
||||
className="inline-flex h-7 min-w-[52px] items-center justify-center rounded-full bg-[#1f8bff] px-3 text-xs font-apple-medium text-white shadow-sm transition hover:bg-[#1478df] disabled:cursor-not-allowed disabled:bg-apple-bg-tertiary disabled:text-apple-label-tertiary disabled:shadow-none"
|
||||
>
|
||||
确认推送
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<ProductPricingWorkspace
|
||||
allRows={productPricingRows}
|
||||
displayedRows={displayedProductPricingRows}
|
||||
inheritedSkuBatchTabs={renderInheritedSkuBatchTabs()}
|
||||
isReadOnly={isReadOnly}
|
||||
isNodeLocked={isProductPricingNodeLocked}
|
||||
canUsePushControls={canUseProductPricingPushControls}
|
||||
onUpdateRow={updateProductPricingRow}
|
||||
onConfirmPushRows={handleConfirmProductPricingPushRows}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function renderArchiveFileTile({
|
||||
file,
|
||||
isActive,
|
||||
|
||||
@ -0,0 +1,272 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import type { NonIntegratedPricingRow } from "./nonIntegratedStorage";
|
||||
import {
|
||||
isProductPricingRowSystemEntered,
|
||||
PRODUCT_PRICING_JST_PUSH_ENABLED,
|
||||
type ProductPricingDisplayRow,
|
||||
} from "./productWorkflowRowHelpers";
|
||||
|
||||
type ProductPricingFieldKey = Exclude<keyof NonIntegratedPricingRow, "skuId">;
|
||||
|
||||
type ProductPricingWorkspaceProps = {
|
||||
allRows: ProductPricingDisplayRow[];
|
||||
displayedRows: ProductPricingDisplayRow[];
|
||||
inheritedSkuBatchTabs: ReactNode;
|
||||
isReadOnly: boolean;
|
||||
isNodeLocked: boolean;
|
||||
canUsePushControls: boolean;
|
||||
onUpdateRow: (
|
||||
skuId: string,
|
||||
key: ProductPricingFieldKey,
|
||||
value: string,
|
||||
) => void;
|
||||
onConfirmPushRows: (rows: ProductPricingDisplayRow[]) => void | Promise<void>;
|
||||
};
|
||||
|
||||
function ReadOnlyCell({ value }: { value: string }) {
|
||||
return (
|
||||
<div className="min-w-[112px] rounded border border-apple-separator bg-apple-bg-secondary px-2 py-1.5 text-xs text-apple-label-primary">
|
||||
{value || "-"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getVirtualInventoryStatusMeta(row: ProductPricingDisplayRow) {
|
||||
const virtualInventorySkipTitle =
|
||||
row.jstVirtualInventoryError || "无需推虚拟库存";
|
||||
const normalizedVirtualInventoryError =
|
||||
row.jstVirtualInventoryError
|
||||
?.replace(/[\u2010-\u2015\u2212\uFE58\uFE63\uFF0D]/g, "-")
|
||||
.replace(/\s+/g, "") ?? "";
|
||||
const isVirtualInventorySkipReason =
|
||||
normalizedVirtualInventoryError.includes("未包含成品代发仓-虚拟库存") ||
|
||||
normalizedVirtualInventoryError.includes("未命中成品代发仓-虚拟库存") ||
|
||||
normalizedVirtualInventoryError.includes("真实库存") ||
|
||||
normalizedVirtualInventoryError.includes("无需推虚拟库存");
|
||||
const skippedMeta = {
|
||||
label: "无需库存",
|
||||
tone: "bg-apple-bg-secondary text-apple-label-secondary",
|
||||
title: virtualInventorySkipTitle,
|
||||
};
|
||||
|
||||
switch (row.jstVirtualInventoryStatus) {
|
||||
case "pending":
|
||||
return {
|
||||
label: "库存待推",
|
||||
tone: "bg-[#e7f1ff] text-[#1764ad]",
|
||||
title: row.jstVirtualInventoryScheduledAt
|
||||
? `计划 ${row.jstVirtualInventoryScheduledAt} 推送`
|
||||
: "已生成虚拟库存待推送",
|
||||
};
|
||||
case "pushed":
|
||||
return {
|
||||
label: "库存已推",
|
||||
tone: "bg-[#dff7e8] text-[#137a38]",
|
||||
title: row.jstVirtualInventoryPushedAt
|
||||
? `已于 ${row.jstVirtualInventoryPushedAt} 推送`
|
||||
: "虚拟库存已推送",
|
||||
};
|
||||
case "failed":
|
||||
if (isVirtualInventorySkipReason) return skippedMeta;
|
||||
return {
|
||||
label: "库存失败",
|
||||
tone: "bg-[#ffe3df] text-[#b42318]",
|
||||
title: row.jstVirtualInventoryError || "虚拟库存推送失败",
|
||||
};
|
||||
case "skipped":
|
||||
return skippedMeta;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function ProductPricingWorkspace({
|
||||
allRows,
|
||||
displayedRows,
|
||||
inheritedSkuBatchTabs,
|
||||
isReadOnly,
|
||||
isNodeLocked,
|
||||
canUsePushControls,
|
||||
onUpdateRow,
|
||||
onConfirmPushRows,
|
||||
}: ProductPricingWorkspaceProps) {
|
||||
const adjustCostPrice = (row: ProductPricingDisplayRow, offset: number) => {
|
||||
if (isReadOnly || isNodeLocked) return;
|
||||
const currentValue = Number(row.costPrice);
|
||||
const nextValue = Math.max(
|
||||
0,
|
||||
(Number.isFinite(currentValue) ? currentValue : 0) + offset,
|
||||
);
|
||||
onUpdateRow(row.skuId, "costPrice", String(nextValue));
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="product-pricing-workspace"
|
||||
className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-apple-xl border border-apple-separator bg-apple-bg-primary"
|
||||
>
|
||||
<div className="border-b border-apple-separator px-3 py-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{inheritedSkuBatchTabs}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="min-h-0 min-w-0 flex-1 overflow-hidden p-3">
|
||||
{allRows.length === 0 ? (
|
||||
<div className="flex min-h-[260px] items-center justify-center rounded-apple-xl border border-dashed border-apple-separator text-sm text-apple-label-secondary">
|
||||
当前还没有 SKU 信息,先维护 SKU 后再核价。
|
||||
</div>
|
||||
) : displayedRows.length === 0 ? (
|
||||
<div className="flex min-h-[260px] items-center justify-center rounded-apple-xl border border-dashed border-apple-separator text-sm text-apple-label-secondary">
|
||||
当前批次还没有 SKU 信息。
|
||||
</div>
|
||||
) : (
|
||||
<div className="non-integrated-table-scroll non-integrated-workspace-table-scroll h-full max-w-full rounded-apple-lg border border-apple-separator">
|
||||
<table className="min-w-[1280px] w-full border-collapse text-xs">
|
||||
<thead className="bg-apple-bg-secondary/40 text-apple-label-secondary">
|
||||
<tr>
|
||||
{[
|
||||
"型号",
|
||||
"货号",
|
||||
"条码",
|
||||
"套件量",
|
||||
"规格名称",
|
||||
"供应商名称",
|
||||
"成本价",
|
||||
"是否录入系统",
|
||||
].map((label) => (
|
||||
<th
|
||||
key={label}
|
||||
className="border-b border-apple-separator px-3 py-2 text-left font-apple-medium"
|
||||
>
|
||||
{label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{displayedRows.map((row) => {
|
||||
const isSystemEntered = isProductPricingRowSystemEntered(row);
|
||||
const pricingReadyForFuturePush =
|
||||
row.costPrice.trim().length > 0;
|
||||
const virtualInventoryStatusMeta =
|
||||
getVirtualInventoryStatusMeta(row);
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={row.skuId}
|
||||
className="border-b border-apple-separator last:border-b-0"
|
||||
>
|
||||
<td className="px-3 py-2">
|
||||
<ReadOnlyCell value={row.model} />
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<ReadOnlyCell value={row.skuCode} />
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<ReadOnlyCell value={row.barcode} />
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<ReadOnlyCell value={row.setCount} />
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<ReadOnlyCell value={row.specName} />
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<ReadOnlyCell value={row.supplierName} />
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<div className="flex min-w-[136px] overflow-hidden rounded border border-apple-separator bg-apple-bg-secondary text-xs text-apple-label-primary">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => adjustCostPrice(row, -1)}
|
||||
disabled={isReadOnly || isNodeLocked}
|
||||
className="h-8 w-9 border-r border-apple-separator text-apple-label-secondary transition hover:bg-apple-bg-primary disabled:opacity-40"
|
||||
aria-label="减少成本价"
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<input
|
||||
value={row.costPrice}
|
||||
onChange={(event) =>
|
||||
onUpdateRow(
|
||||
row.skuId,
|
||||
"costPrice",
|
||||
event.target.value,
|
||||
)
|
||||
}
|
||||
readOnly={isReadOnly || isNodeLocked}
|
||||
placeholder="0"
|
||||
className="h-8 w-16 bg-transparent px-2 text-center outline-none placeholder:text-apple-label-tertiary"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => adjustCostPrice(row, 1)}
|
||||
disabled={isReadOnly || isNodeLocked}
|
||||
className="h-8 w-9 border-l border-apple-separator text-apple-label-secondary transition hover:bg-apple-bg-primary disabled:opacity-40"
|
||||
aria-label="增加成本价"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<div className="flex min-w-[236px] items-center gap-2">
|
||||
<span
|
||||
className={`inline-flex h-6 min-w-[52px] items-center justify-center rounded-full px-2 text-[11px] font-apple-medium ${
|
||||
isSystemEntered
|
||||
? "bg-[#dff7e8] text-[#137a38]"
|
||||
: "bg-[#fff3df] text-[#9b6511]"
|
||||
}`}
|
||||
>
|
||||
{isSystemEntered ? "已录入" : "未录入"}
|
||||
</span>
|
||||
{virtualInventoryStatusMeta ? (
|
||||
<span
|
||||
className={`inline-flex h-6 min-w-[64px] items-center justify-center rounded-full px-2 text-[11px] font-apple-medium ${virtualInventoryStatusMeta.tone}`}
|
||||
title={virtualInventoryStatusMeta.title}
|
||||
>
|
||||
{virtualInventoryStatusMeta.label}
|
||||
</span>
|
||||
) : null}
|
||||
{canUsePushControls ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void onConfirmPushRows([row])}
|
||||
disabled={
|
||||
isReadOnly ||
|
||||
isNodeLocked ||
|
||||
isSystemEntered ||
|
||||
!pricingReadyForFuturePush ||
|
||||
!PRODUCT_PRICING_JST_PUSH_ENABLED
|
||||
}
|
||||
title={
|
||||
isSystemEntered
|
||||
? "当前 SKU 已录入系统,后续以聚水潭回传状态为准"
|
||||
: PRODUCT_PRICING_JST_PUSH_ENABLED &&
|
||||
pricingReadyForFuturePush
|
||||
? "确认推送当前 SKU 到聚水潭"
|
||||
: pricingReadyForFuturePush
|
||||
? "聚水潭接口未接入,暂不可确认推送"
|
||||
: "请先填写成本价"
|
||||
}
|
||||
className="inline-flex h-7 min-w-[52px] items-center justify-center rounded-full bg-[#1f8bff] px-3 text-xs font-apple-medium text-white shadow-sm transition hover:bg-[#1478df] disabled:cursor-not-allowed disabled:bg-apple-bg-tertiary disabled:text-apple-label-tertiary disabled:shadow-none"
|
||||
>
|
||||
确认推送
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user