refactor: Improve table header and cell styling for production plan management and item inspection pages
- Updated the table header and cell styles to enhance visibility and usability, including adjustments to z-index and sticky positioning. - Implemented dynamic label mapping for inspection types in the item inspection page to improve clarity. - Enhanced the sales order page by including management item filters in server queries, allowing for better data handling and user experience. - These changes aim to provide a more intuitive interface and improve data representation across multiple company implementations.
This commit is contained in:
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
||||
|
||||
return (
|
||||
<Table style={{ minWidth: "900px" }}>
|
||||
<TableHeader className="sticky top-0 z-10">
|
||||
<TableHeader className="sticky top-0 z-20">
|
||||
<TableRow className="bg-muted hover:bg-muted">
|
||||
<TableHead style={{ width: "30px", minWidth: "30px" }}>
|
||||
<TableHead className="sticky left-0 z-20 bg-muted" style={{ width: "30px", minWidth: "30px" }}>
|
||||
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||
</TableHead>
|
||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
||||
<TableHead className="sticky left-[30px] z-20 bg-muted" style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead className="sticky left-[70px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground" style={{ width: "140px", minWidth: "140px" }}>품목코드</TableHead>
|
||||
<TableHead className="sticky left-[210px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground border-r" style={{ width: "140px", minWidth: "140px" }}>품목명</TableHead>
|
||||
{ts.visibleColumns.map((col) => (
|
||||
<TableHead key={col.key} style={{ ...ts.thStyle(col.key), minWidth: "80px" }} className="text-xs font-bold whitespace-nowrap text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
|
||||
{col.label}
|
||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
||||
if (item._isGroupSummary) {
|
||||
return (
|
||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||
{ts.visibleColumns.map((col) => {
|
||||
const v = (item as any)[col.key];
|
||||
return (
|
||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
||||
return (
|
||||
<React.Fragment key={item.item_code || rowIdx}>
|
||||
<TableRow className={cn("cursor-pointer border-t-2 border-t-primary/30 bg-primary/5 font-semibold hover:bg-primary/10", selectedItemGroups.has(item.item_code) && "bg-primary/10")}>
|
||||
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<TableCell className="sticky left-0 z-10 bg-background text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||
</TableCell>
|
||||
<TableCell className="text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<TableCell className="sticky left-[30px] z-10 bg-background text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
<TableCell className="sticky left-[70px] z-10 bg-background text-[13px] text-primary truncate" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell className="sticky left-[210px] z-10 bg-background text-[13px] text-primary truncate border-r" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||
</TableRow>
|
||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
||||
}
|
||||
return (
|
||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} className="pl-10">
|
||||
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="text-muted-foreground">수주번호:</span>
|
||||
<span className="font-medium">{detail.order_no}</span>
|
||||
|
||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
||||
case "inspection_type": return (
|
||||
<TableCell key={col.key}>
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
{group.types.map((t: string) => (
|
||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
||||
))}
|
||||
{group.types.map((t: string) => {
|
||||
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||
})}
|
||||
</div>
|
||||
</TableCell>
|
||||
);
|
||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||
)}
|
||||
>
|
||||
{type}
|
||||
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||
<span className={cn(
|
||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
||||
selectedTypeTab === type ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted-foreground/20 text-muted-foreground"
|
||||
|
||||
@@ -624,6 +624,15 @@ export default function SalesOrderPage() {
|
||||
const filters: any[] = [];
|
||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
||||
|
||||
// 관리품목 필터를 서버 쿼리에 포함 (코드 + 라벨 양쪽 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
// 코드 또는 라벨이 저장된 경우 모두 조회하기 위해 in 연산자 사용
|
||||
const divValues = [itemSearchDivision];
|
||||
if (divLabel) divValues.push(divLabel);
|
||||
filters.push({ columnName: "division", operator: "in", value: divValues });
|
||||
}
|
||||
|
||||
// 거래처우선 단가방식일 때 거래처에 연결된 품목만 필터링
|
||||
const isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||
const partnerId = masterForm.partner_id;
|
||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
||||
if (isCustomerPrice && partnerId) {
|
||||
try {
|
||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
||||
page: 1, size: 500,
|
||||
page: 1, size: 5000,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
|
||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
||||
page: 1, size: 500,
|
||||
page: p, size: s,
|
||||
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||
autoFilter: true,
|
||||
});
|
||||
const resData = res.data?.data;
|
||||
let allRows = resData?.data || resData?.rows || [];
|
||||
let rows = resData?.data || resData?.rows || [];
|
||||
const serverTotal = resData?.total || resData?.totalCount || rows.length;
|
||||
|
||||
// 거래처우선일 때 연결된 품목만 표시
|
||||
// 거래처우선일 때 연결된 품목만 표시 (클라이언트 필터)
|
||||
if (customerItemIds) {
|
||||
allRows = allRows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
rows = rows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
}
|
||||
|
||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
allRows = allRows.filter((item: any) => {
|
||||
const div = item.division || "";
|
||||
return div.includes(itemSearchDivision) || (divLabel && div.includes(divLabel));
|
||||
});
|
||||
}
|
||||
const total = allRows.length;
|
||||
const start = (p - 1) * s;
|
||||
setItemSearchResults(allRows.slice(start, start + s));
|
||||
setItemTotal(total);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(total / s)));
|
||||
setItemSearchResults(rows);
|
||||
setItemTotal(serverTotal);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||
} catch { /* skip */ } finally {
|
||||
setItemSearchLoading(false);
|
||||
}
|
||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||
className="h-9 flex-1"
|
||||
/>
|
||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
||||
<SelectTrigger className="h-9 w-[130px]"><SelectValue placeholder="관리품목" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">전체</SelectItem>
|
||||
{(categoryOptions["item_division"] || []).map((o) => (
|
||||
<SelectItem key={o.code} value={o.code}>{o.label}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="h-9 px-3 flex items-center rounded-md border border-input bg-muted text-xs font-medium text-muted-foreground whitespace-nowrap">영업관리</div>
|
||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
||||
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||
</Button>
|
||||
|
||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
||||
|
||||
return (
|
||||
<Table style={{ minWidth: "900px" }}>
|
||||
<TableHeader className="sticky top-0 z-10">
|
||||
<TableHeader className="sticky top-0 z-20">
|
||||
<TableRow className="bg-muted hover:bg-muted">
|
||||
<TableHead style={{ width: "30px", minWidth: "30px" }}>
|
||||
<TableHead className="sticky left-0 z-20 bg-muted" style={{ width: "30px", minWidth: "30px" }}>
|
||||
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||
</TableHead>
|
||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
||||
<TableHead className="sticky left-[30px] z-20 bg-muted" style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead className="sticky left-[70px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground" style={{ width: "140px", minWidth: "140px" }}>품목코드</TableHead>
|
||||
<TableHead className="sticky left-[210px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground border-r" style={{ width: "140px", minWidth: "140px" }}>품목명</TableHead>
|
||||
{ts.visibleColumns.map((col) => (
|
||||
<TableHead key={col.key} style={{ ...ts.thStyle(col.key), minWidth: "80px" }} className="text-xs font-bold whitespace-nowrap text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
|
||||
{col.label}
|
||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
||||
if (item._isGroupSummary) {
|
||||
return (
|
||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||
{ts.visibleColumns.map((col) => {
|
||||
const v = (item as any)[col.key];
|
||||
return (
|
||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
||||
return (
|
||||
<React.Fragment key={item.item_code || rowIdx}>
|
||||
<TableRow className={cn("cursor-pointer border-t-2 border-t-primary/30 bg-primary/5 font-semibold hover:bg-primary/10", selectedItemGroups.has(item.item_code) && "bg-primary/10")}>
|
||||
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<TableCell className="sticky left-0 z-10 bg-background text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||
</TableCell>
|
||||
<TableCell className="text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<TableCell className="sticky left-[30px] z-10 bg-background text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
<TableCell className="sticky left-[70px] z-10 bg-background text-[13px] text-primary truncate" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell className="sticky left-[210px] z-10 bg-background text-[13px] text-primary truncate border-r" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||
</TableRow>
|
||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
||||
}
|
||||
return (
|
||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} className="pl-10">
|
||||
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="text-muted-foreground">수주번호:</span>
|
||||
<span className="font-medium">{detail.order_no}</span>
|
||||
|
||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
||||
case "inspection_type": return (
|
||||
<TableCell key={col.key}>
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
{group.types.map((t: string) => (
|
||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
||||
))}
|
||||
{group.types.map((t: string) => {
|
||||
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||
})}
|
||||
</div>
|
||||
</TableCell>
|
||||
);
|
||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||
)}
|
||||
>
|
||||
{type}
|
||||
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||
<span className={cn(
|
||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
||||
selectedTypeTab === type ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted-foreground/20 text-muted-foreground"
|
||||
|
||||
@@ -624,6 +624,15 @@ export default function SalesOrderPage() {
|
||||
const filters: any[] = [];
|
||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
||||
|
||||
// 관리품목 필터를 서버 쿼리에 포함 (코드 + 라벨 양쪽 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
// 코드 또는 라벨이 저장된 경우 모두 조회하기 위해 in 연산자 사용
|
||||
const divValues = [itemSearchDivision];
|
||||
if (divLabel) divValues.push(divLabel);
|
||||
filters.push({ columnName: "division", operator: "in", value: divValues });
|
||||
}
|
||||
|
||||
// 거래처우선 단가방식일 때 거래처에 연결된 품목만 필터링
|
||||
const isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||
const partnerId = masterForm.partner_id;
|
||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
||||
if (isCustomerPrice && partnerId) {
|
||||
try {
|
||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
||||
page: 1, size: 500,
|
||||
page: 1, size: 5000,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
|
||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
||||
page: 1, size: 500,
|
||||
page: p, size: s,
|
||||
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||
autoFilter: true,
|
||||
});
|
||||
const resData = res.data?.data;
|
||||
let allRows = resData?.data || resData?.rows || [];
|
||||
let rows = resData?.data || resData?.rows || [];
|
||||
const serverTotal = resData?.total || resData?.totalCount || rows.length;
|
||||
|
||||
// 거래처우선일 때 연결된 품목만 표시
|
||||
// 거래처우선일 때 연결된 품목만 표시 (클라이언트 필터)
|
||||
if (customerItemIds) {
|
||||
allRows = allRows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
rows = rows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
}
|
||||
|
||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
allRows = allRows.filter((item: any) => {
|
||||
const div = item.division || "";
|
||||
return div.includes(itemSearchDivision) || (divLabel && div.includes(divLabel));
|
||||
});
|
||||
}
|
||||
const total = allRows.length;
|
||||
const start = (p - 1) * s;
|
||||
setItemSearchResults(allRows.slice(start, start + s));
|
||||
setItemTotal(total);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(total / s)));
|
||||
setItemSearchResults(rows);
|
||||
setItemTotal(serverTotal);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||
} catch { /* skip */ } finally {
|
||||
setItemSearchLoading(false);
|
||||
}
|
||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||
className="h-9 flex-1"
|
||||
/>
|
||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
||||
<SelectTrigger className="h-9 w-[130px]"><SelectValue placeholder="관리품목" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">전체</SelectItem>
|
||||
{(categoryOptions["item_division"] || []).map((o) => (
|
||||
<SelectItem key={o.code} value={o.code}>{o.label}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="h-9 px-3 flex items-center rounded-md border border-input bg-muted text-xs font-medium text-muted-foreground whitespace-nowrap">영업관리</div>
|
||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
||||
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||
</Button>
|
||||
|
||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
||||
|
||||
return (
|
||||
<Table style={{ minWidth: "900px" }}>
|
||||
<TableHeader className="sticky top-0 z-10">
|
||||
<TableHeader className="sticky top-0 z-20">
|
||||
<TableRow className="bg-muted hover:bg-muted">
|
||||
<TableHead style={{ width: "30px", minWidth: "30px" }}>
|
||||
<TableHead className="sticky left-0 z-20 bg-muted" style={{ width: "30px", minWidth: "30px" }}>
|
||||
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||
</TableHead>
|
||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
||||
<TableHead className="sticky left-[30px] z-20 bg-muted" style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead className="sticky left-[70px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground" style={{ width: "140px", minWidth: "140px" }}>품목코드</TableHead>
|
||||
<TableHead className="sticky left-[210px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground border-r" style={{ width: "140px", minWidth: "140px" }}>품목명</TableHead>
|
||||
{ts.visibleColumns.map((col) => (
|
||||
<TableHead key={col.key} style={{ ...ts.thStyle(col.key), minWidth: "80px" }} className="text-xs font-bold whitespace-nowrap text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
|
||||
{col.label}
|
||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
||||
if (item._isGroupSummary) {
|
||||
return (
|
||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||
{ts.visibleColumns.map((col) => {
|
||||
const v = (item as any)[col.key];
|
||||
return (
|
||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
||||
return (
|
||||
<React.Fragment key={item.item_code || rowIdx}>
|
||||
<TableRow className={cn("cursor-pointer border-t-2 border-t-primary/30 bg-primary/5 font-semibold hover:bg-primary/10", selectedItemGroups.has(item.item_code) && "bg-primary/10")}>
|
||||
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<TableCell className="sticky left-0 z-10 bg-background text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||
</TableCell>
|
||||
<TableCell className="text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<TableCell className="sticky left-[30px] z-10 bg-background text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
<TableCell className="sticky left-[70px] z-10 bg-background text-[13px] text-primary truncate" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell className="sticky left-[210px] z-10 bg-background text-[13px] text-primary truncate border-r" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||
</TableRow>
|
||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
||||
}
|
||||
return (
|
||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} className="pl-10">
|
||||
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="text-muted-foreground">수주번호:</span>
|
||||
<span className="font-medium">{detail.order_no}</span>
|
||||
|
||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
||||
case "inspection_type": return (
|
||||
<TableCell key={col.key}>
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
{group.types.map((t: string) => (
|
||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
||||
))}
|
||||
{group.types.map((t: string) => {
|
||||
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||
})}
|
||||
</div>
|
||||
</TableCell>
|
||||
);
|
||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||
)}
|
||||
>
|
||||
{type}
|
||||
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||
<span className={cn(
|
||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
||||
selectedTypeTab === type ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted-foreground/20 text-muted-foreground"
|
||||
|
||||
@@ -624,6 +624,15 @@ export default function SalesOrderPage() {
|
||||
const filters: any[] = [];
|
||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
||||
|
||||
// 관리품목 필터를 서버 쿼리에 포함 (코드 + 라벨 양쪽 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
// 코드 또는 라벨이 저장된 경우 모두 조회하기 위해 in 연산자 사용
|
||||
const divValues = [itemSearchDivision];
|
||||
if (divLabel) divValues.push(divLabel);
|
||||
filters.push({ columnName: "division", operator: "in", value: divValues });
|
||||
}
|
||||
|
||||
// 거래처우선 단가방식일 때 거래처에 연결된 품목만 필터링
|
||||
const isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||
const partnerId = masterForm.partner_id;
|
||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
||||
if (isCustomerPrice && partnerId) {
|
||||
try {
|
||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
||||
page: 1, size: 500,
|
||||
page: 1, size: 5000,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
|
||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
||||
page: 1, size: 500,
|
||||
page: p, size: s,
|
||||
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||
autoFilter: true,
|
||||
});
|
||||
const resData = res.data?.data;
|
||||
let allRows = resData?.data || resData?.rows || [];
|
||||
let rows = resData?.data || resData?.rows || [];
|
||||
const serverTotal = resData?.total || resData?.totalCount || rows.length;
|
||||
|
||||
// 거래처우선일 때 연결된 품목만 표시
|
||||
// 거래처우선일 때 연결된 품목만 표시 (클라이언트 필터)
|
||||
if (customerItemIds) {
|
||||
allRows = allRows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
rows = rows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
}
|
||||
|
||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
allRows = allRows.filter((item: any) => {
|
||||
const div = item.division || "";
|
||||
return div.includes(itemSearchDivision) || (divLabel && div.includes(divLabel));
|
||||
});
|
||||
}
|
||||
const total = allRows.length;
|
||||
const start = (p - 1) * s;
|
||||
setItemSearchResults(allRows.slice(start, start + s));
|
||||
setItemTotal(total);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(total / s)));
|
||||
setItemSearchResults(rows);
|
||||
setItemTotal(serverTotal);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||
} catch { /* skip */ } finally {
|
||||
setItemSearchLoading(false);
|
||||
}
|
||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||
className="h-9 flex-1"
|
||||
/>
|
||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
||||
<SelectTrigger className="h-9 w-[130px]"><SelectValue placeholder="관리품목" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">전체</SelectItem>
|
||||
{(categoryOptions["item_division"] || []).map((o) => (
|
||||
<SelectItem key={o.code} value={o.code}>{o.label}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="h-9 px-3 flex items-center rounded-md border border-input bg-muted text-xs font-medium text-muted-foreground whitespace-nowrap">영업관리</div>
|
||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
||||
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||
</Button>
|
||||
|
||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
||||
|
||||
return (
|
||||
<Table style={{ minWidth: "900px" }}>
|
||||
<TableHeader className="sticky top-0 z-10">
|
||||
<TableHeader className="sticky top-0 z-20">
|
||||
<TableRow className="bg-muted hover:bg-muted">
|
||||
<TableHead style={{ width: "30px", minWidth: "30px" }}>
|
||||
<TableHead className="sticky left-0 z-20 bg-muted" style={{ width: "30px", minWidth: "30px" }}>
|
||||
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||
</TableHead>
|
||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
||||
<TableHead className="sticky left-[30px] z-20 bg-muted" style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead className="sticky left-[70px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground" style={{ width: "140px", minWidth: "140px" }}>품목코드</TableHead>
|
||||
<TableHead className="sticky left-[210px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground border-r" style={{ width: "140px", minWidth: "140px" }}>품목명</TableHead>
|
||||
{ts.visibleColumns.map((col) => (
|
||||
<TableHead key={col.key} style={{ ...ts.thStyle(col.key), minWidth: "80px" }} className="text-xs font-bold whitespace-nowrap text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
|
||||
{col.label}
|
||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
||||
if (item._isGroupSummary) {
|
||||
return (
|
||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||
{ts.visibleColumns.map((col) => {
|
||||
const v = (item as any)[col.key];
|
||||
return (
|
||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
||||
return (
|
||||
<React.Fragment key={item.item_code || rowIdx}>
|
||||
<TableRow className={cn("cursor-pointer border-t-2 border-t-primary/30 bg-primary/5 font-semibold hover:bg-primary/10", selectedItemGroups.has(item.item_code) && "bg-primary/10")}>
|
||||
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<TableCell className="sticky left-0 z-10 bg-background text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||
</TableCell>
|
||||
<TableCell className="text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<TableCell className="sticky left-[30px] z-10 bg-background text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
<TableCell className="sticky left-[70px] z-10 bg-background text-[13px] text-primary truncate" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell className="sticky left-[210px] z-10 bg-background text-[13px] text-primary truncate border-r" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||
</TableRow>
|
||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
||||
}
|
||||
return (
|
||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} className="pl-10">
|
||||
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="text-muted-foreground">수주번호:</span>
|
||||
<span className="font-medium">{detail.order_no}</span>
|
||||
|
||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
||||
case "inspection_type": return (
|
||||
<TableCell key={col.key}>
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
{group.types.map((t: string) => (
|
||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
||||
))}
|
||||
{group.types.map((t: string) => {
|
||||
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||
})}
|
||||
</div>
|
||||
</TableCell>
|
||||
);
|
||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||
)}
|
||||
>
|
||||
{type}
|
||||
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||
<span className={cn(
|
||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
||||
selectedTypeTab === type ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted-foreground/20 text-muted-foreground"
|
||||
|
||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
||||
|
||||
return (
|
||||
<Table style={{ minWidth: "900px" }}>
|
||||
<TableHeader className="sticky top-0 z-10">
|
||||
<TableHeader className="sticky top-0 z-20">
|
||||
<TableRow className="bg-muted hover:bg-muted">
|
||||
<TableHead style={{ width: "30px", minWidth: "30px" }}>
|
||||
<TableHead className="sticky left-0 z-20 bg-muted" style={{ width: "30px", minWidth: "30px" }}>
|
||||
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||
</TableHead>
|
||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
||||
<TableHead className="sticky left-[30px] z-20 bg-muted" style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead className="sticky left-[70px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground" style={{ width: "140px", minWidth: "140px" }}>품목코드</TableHead>
|
||||
<TableHead className="sticky left-[210px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground border-r" style={{ width: "140px", minWidth: "140px" }}>품목명</TableHead>
|
||||
{ts.visibleColumns.map((col) => (
|
||||
<TableHead key={col.key} style={{ ...ts.thStyle(col.key), minWidth: "80px" }} className="text-xs font-bold whitespace-nowrap text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
|
||||
{col.label}
|
||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
||||
if (item._isGroupSummary) {
|
||||
return (
|
||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||
{ts.visibleColumns.map((col) => {
|
||||
const v = (item as any)[col.key];
|
||||
return (
|
||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
||||
return (
|
||||
<React.Fragment key={item.item_code || rowIdx}>
|
||||
<TableRow className={cn("cursor-pointer border-t-2 border-t-primary/30 bg-primary/5 font-semibold hover:bg-primary/10", selectedItemGroups.has(item.item_code) && "bg-primary/10")}>
|
||||
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<TableCell className="sticky left-0 z-10 bg-background text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||
</TableCell>
|
||||
<TableCell className="text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<TableCell className="sticky left-[30px] z-10 bg-background text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
<TableCell className="sticky left-[70px] z-10 bg-background text-[13px] text-primary truncate" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell className="sticky left-[210px] z-10 bg-background text-[13px] text-primary truncate border-r" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||
</TableRow>
|
||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
||||
}
|
||||
return (
|
||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} className="pl-10">
|
||||
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="text-muted-foreground">수주번호:</span>
|
||||
<span className="font-medium">{detail.order_no}</span>
|
||||
|
||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
||||
case "inspection_type": return (
|
||||
<TableCell key={col.key}>
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
{group.types.map((t: string) => (
|
||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
||||
))}
|
||||
{group.types.map((t: string) => {
|
||||
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||
})}
|
||||
</div>
|
||||
</TableCell>
|
||||
);
|
||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||
)}
|
||||
>
|
||||
{type}
|
||||
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||
<span className={cn(
|
||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
||||
selectedTypeTab === type ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted-foreground/20 text-muted-foreground"
|
||||
|
||||
@@ -624,6 +624,15 @@ export default function SalesOrderPage() {
|
||||
const filters: any[] = [];
|
||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
||||
|
||||
// 관리품목 필터를 서버 쿼리에 포함 (코드 + 라벨 양쪽 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
// 코드 또는 라벨이 저장된 경우 모두 조회하기 위해 in 연산자 사용
|
||||
const divValues = [itemSearchDivision];
|
||||
if (divLabel) divValues.push(divLabel);
|
||||
filters.push({ columnName: "division", operator: "in", value: divValues });
|
||||
}
|
||||
|
||||
// 거래처우선 단가방식일 때 거래처에 연결된 품목만 필터링
|
||||
const isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||
const partnerId = masterForm.partner_id;
|
||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
||||
if (isCustomerPrice && partnerId) {
|
||||
try {
|
||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
||||
page: 1, size: 500,
|
||||
page: 1, size: 5000,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
|
||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
||||
page: 1, size: 500,
|
||||
page: p, size: s,
|
||||
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||
autoFilter: true,
|
||||
});
|
||||
const resData = res.data?.data;
|
||||
let allRows = resData?.data || resData?.rows || [];
|
||||
let rows = resData?.data || resData?.rows || [];
|
||||
const serverTotal = resData?.total || resData?.totalCount || rows.length;
|
||||
|
||||
// 거래처우선일 때 연결된 품목만 표시
|
||||
// 거래처우선일 때 연결된 품목만 표시 (클라이언트 필터)
|
||||
if (customerItemIds) {
|
||||
allRows = allRows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
rows = rows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
}
|
||||
|
||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
allRows = allRows.filter((item: any) => {
|
||||
const div = item.division || "";
|
||||
return div.includes(itemSearchDivision) || (divLabel && div.includes(divLabel));
|
||||
});
|
||||
}
|
||||
const total = allRows.length;
|
||||
const start = (p - 1) * s;
|
||||
setItemSearchResults(allRows.slice(start, start + s));
|
||||
setItemTotal(total);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(total / s)));
|
||||
setItemSearchResults(rows);
|
||||
setItemTotal(serverTotal);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||
} catch { /* skip */ } finally {
|
||||
setItemSearchLoading(false);
|
||||
}
|
||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||
className="h-9 flex-1"
|
||||
/>
|
||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
||||
<SelectTrigger className="h-9 w-[130px]"><SelectValue placeholder="관리품목" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">전체</SelectItem>
|
||||
{(categoryOptions["item_division"] || []).map((o) => (
|
||||
<SelectItem key={o.code} value={o.code}>{o.label}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="h-9 px-3 flex items-center rounded-md border border-input bg-muted text-xs font-medium text-muted-foreground whitespace-nowrap">영업관리</div>
|
||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
||||
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||
</Button>
|
||||
|
||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
||||
|
||||
return (
|
||||
<Table style={{ minWidth: "900px" }}>
|
||||
<TableHeader className="sticky top-0 z-10">
|
||||
<TableHeader className="sticky top-0 z-20">
|
||||
<TableRow className="bg-muted hover:bg-muted">
|
||||
<TableHead style={{ width: "30px", minWidth: "30px" }}>
|
||||
<TableHead className="sticky left-0 z-20 bg-muted" style={{ width: "30px", minWidth: "30px" }}>
|
||||
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||
</TableHead>
|
||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
||||
<TableHead className="sticky left-[30px] z-20 bg-muted" style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead className="sticky left-[70px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground" style={{ width: "140px", minWidth: "140px" }}>품목코드</TableHead>
|
||||
<TableHead className="sticky left-[210px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground border-r" style={{ width: "140px", minWidth: "140px" }}>품목명</TableHead>
|
||||
{ts.visibleColumns.map((col) => (
|
||||
<TableHead key={col.key} style={{ ...ts.thStyle(col.key), minWidth: "80px" }} className="text-xs font-bold whitespace-nowrap text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
|
||||
{col.label}
|
||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
||||
if (item._isGroupSummary) {
|
||||
return (
|
||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||
{ts.visibleColumns.map((col) => {
|
||||
const v = (item as any)[col.key];
|
||||
return (
|
||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
||||
return (
|
||||
<React.Fragment key={item.item_code || rowIdx}>
|
||||
<TableRow className={cn("cursor-pointer border-t-2 border-t-primary/30 bg-primary/5 font-semibold hover:bg-primary/10", selectedItemGroups.has(item.item_code) && "bg-primary/10")}>
|
||||
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<TableCell className="sticky left-0 z-10 bg-background text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||
</TableCell>
|
||||
<TableCell className="text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<TableCell className="sticky left-[30px] z-10 bg-background text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
<TableCell className="sticky left-[70px] z-10 bg-background text-[13px] text-primary truncate" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell className="sticky left-[210px] z-10 bg-background text-[13px] text-primary truncate border-r" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||
</TableRow>
|
||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
||||
}
|
||||
return (
|
||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} className="pl-10">
|
||||
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="text-muted-foreground">수주번호:</span>
|
||||
<span className="font-medium">{detail.order_no}</span>
|
||||
|
||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
||||
case "inspection_type": return (
|
||||
<TableCell key={col.key}>
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
{group.types.map((t: string) => (
|
||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
||||
))}
|
||||
{group.types.map((t: string) => {
|
||||
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||
})}
|
||||
</div>
|
||||
</TableCell>
|
||||
);
|
||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||
)}
|
||||
>
|
||||
{type}
|
||||
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||
<span className={cn(
|
||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
||||
selectedTypeTab === type ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted-foreground/20 text-muted-foreground"
|
||||
|
||||
@@ -624,6 +624,15 @@ export default function SalesOrderPage() {
|
||||
const filters: any[] = [];
|
||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
||||
|
||||
// 관리품목 필터를 서버 쿼리에 포함 (코드 + 라벨 양쪽 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
// 코드 또는 라벨이 저장된 경우 모두 조회하기 위해 in 연산자 사용
|
||||
const divValues = [itemSearchDivision];
|
||||
if (divLabel) divValues.push(divLabel);
|
||||
filters.push({ columnName: "division", operator: "in", value: divValues });
|
||||
}
|
||||
|
||||
// 거래처우선 단가방식일 때 거래처에 연결된 품목만 필터링
|
||||
const isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||
const partnerId = masterForm.partner_id;
|
||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
||||
if (isCustomerPrice && partnerId) {
|
||||
try {
|
||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
||||
page: 1, size: 500,
|
||||
page: 1, size: 5000,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
|
||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
||||
page: 1, size: 500,
|
||||
page: p, size: s,
|
||||
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||
autoFilter: true,
|
||||
});
|
||||
const resData = res.data?.data;
|
||||
let allRows = resData?.data || resData?.rows || [];
|
||||
let rows = resData?.data || resData?.rows || [];
|
||||
const serverTotal = resData?.total || resData?.totalCount || rows.length;
|
||||
|
||||
// 거래처우선일 때 연결된 품목만 표시
|
||||
// 거래처우선일 때 연결된 품목만 표시 (클라이언트 필터)
|
||||
if (customerItemIds) {
|
||||
allRows = allRows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
rows = rows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id));
|
||||
}
|
||||
|
||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
||||
if (itemSearchDivision !== "all") {
|
||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
||||
allRows = allRows.filter((item: any) => {
|
||||
const div = item.division || "";
|
||||
return div.includes(itemSearchDivision) || (divLabel && div.includes(divLabel));
|
||||
});
|
||||
}
|
||||
const total = allRows.length;
|
||||
const start = (p - 1) * s;
|
||||
setItemSearchResults(allRows.slice(start, start + s));
|
||||
setItemTotal(total);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(total / s)));
|
||||
setItemSearchResults(rows);
|
||||
setItemTotal(serverTotal);
|
||||
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||
} catch { /* skip */ } finally {
|
||||
setItemSearchLoading(false);
|
||||
}
|
||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||
className="h-9 flex-1"
|
||||
/>
|
||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
||||
<SelectTrigger className="h-9 w-[130px]"><SelectValue placeholder="관리품목" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">전체</SelectItem>
|
||||
{(categoryOptions["item_division"] || []).map((o) => (
|
||||
<SelectItem key={o.code} value={o.code}>{o.label}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="h-9 px-3 flex items-center rounded-md border border-input bg-muted text-xs font-medium text-muted-foreground whitespace-nowrap">영업관리</div>
|
||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
||||
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||
</Button>
|
||||
|
||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
||||
|
||||
return (
|
||||
<Table style={{ minWidth: "900px" }}>
|
||||
<TableHeader className="sticky top-0 z-10">
|
||||
<TableHeader className="sticky top-0 z-20">
|
||||
<TableRow className="bg-muted hover:bg-muted">
|
||||
<TableHead style={{ width: "30px", minWidth: "30px" }}>
|
||||
<TableHead className="sticky left-0 z-20 bg-muted" style={{ width: "30px", minWidth: "30px" }}>
|
||||
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||
</TableHead>
|
||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
||||
<TableHead style={{ width: "140px", minWidth: "140px" }} className="text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
||||
<TableHead className="sticky left-[30px] z-20 bg-muted" style={{ width: "40px", minWidth: "40px" }} />
|
||||
<TableHead className="sticky left-[70px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground" style={{ width: "140px", minWidth: "140px" }}>품목코드</TableHead>
|
||||
<TableHead className="sticky left-[210px] z-20 bg-muted text-xs font-bold whitespace-nowrap text-[11px] font-bold uppercase tracking-wide text-muted-foreground border-r" style={{ width: "140px", minWidth: "140px" }}>품목명</TableHead>
|
||||
{ts.visibleColumns.map((col) => (
|
||||
<TableHead key={col.key} style={{ ...ts.thStyle(col.key), minWidth: "80px" }} className="text-xs font-bold whitespace-nowrap text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
|
||||
{col.label}
|
||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
||||
if (item._isGroupSummary) {
|
||||
return (
|
||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||
{ts.visibleColumns.map((col) => {
|
||||
const v = (item as any)[col.key];
|
||||
return (
|
||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
||||
return (
|
||||
<React.Fragment key={item.item_code || rowIdx}>
|
||||
<TableRow className={cn("cursor-pointer border-t-2 border-t-primary/30 bg-primary/5 font-semibold hover:bg-primary/10", selectedItemGroups.has(item.item_code) && "bg-primary/10")}>
|
||||
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<TableCell className="sticky left-0 z-10 bg-background text-center" onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||
</TableCell>
|
||||
<TableCell className="text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<TableCell className="sticky left-[30px] z-10 bg-background text-center" onClick={() => toggleItemExpand(item.item_code)}>
|
||||
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
<TableCell className="sticky left-[70px] z-10 bg-background text-[13px] text-primary truncate" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</TableCell>
|
||||
<TableCell className="sticky left-[210px] z-10 bg-background text-[13px] text-primary truncate border-r" style={{ width: "140px", minWidth: "140px" }} onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</TableCell>
|
||||
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||
</TableRow>
|
||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
||||
}
|
||||
return (
|
||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||
<TableCell />
|
||||
<TableCell />
|
||||
<TableCell colSpan={2} className="pl-10">
|
||||
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="text-muted-foreground">수주번호:</span>
|
||||
<span className="font-medium">{detail.order_no}</span>
|
||||
|
||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
||||
case "inspection_type": return (
|
||||
<TableCell key={col.key}>
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
{group.types.map((t: string) => (
|
||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
||||
))}
|
||||
{group.types.map((t: string) => {
|
||||
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||
})}
|
||||
</div>
|
||||
</TableCell>
|
||||
);
|
||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||
)}
|
||||
>
|
||||
{type}
|
||||
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||
<span className={cn(
|
||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
||||
selectedTypeTab === type ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted-foreground/20 text-muted-foreground"
|
||||
|
||||
Reference in New Issue
Block a user