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 (
|
return (
|
||||||
<Table style={{ minWidth: "900px" }}>
|
<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">
|
<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" />
|
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
<TableHead className="sticky left-[30px] z-20 bg-muted" 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 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 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-[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) => (
|
{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">
|
<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}
|
{col.label}
|
||||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
if (item._isGroupSummary) {
|
if (item._isGroupSummary) {
|
||||||
return (
|
return (
|
||||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||||
<TableCell colSpan={2} />
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||||
{ts.visibleColumns.map((col) => {
|
{ts.visibleColumns.map((col) => {
|
||||||
const v = (item as any)[col.key];
|
const v = (item as any)[col.key];
|
||||||
return (
|
return (
|
||||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={item.item_code || rowIdx}>
|
<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")}>
|
<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" />
|
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||||
</TableCell>
|
</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")} />
|
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</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 style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</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))}
|
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||||
<TableCell colSpan={2} className="pl-10">
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<span className="text-muted-foreground">수주번호:</span>
|
<span className="text-muted-foreground">수주번호:</span>
|
||||||
<span className="font-medium">{detail.order_no}</span>
|
<span className="font-medium">{detail.order_no}</span>
|
||||||
|
|||||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
|||||||
case "inspection_type": return (
|
case "inspection_type": return (
|
||||||
<TableCell key={col.key}>
|
<TableCell key={col.key}>
|
||||||
<div className="flex gap-1 flex-wrap">
|
<div className="flex gap-1 flex-wrap">
|
||||||
{group.types.map((t: string) => (
|
{group.types.map((t: string) => {
|
||||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||||
))}
|
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
|||||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{type}
|
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
"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"
|
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[] = [];
|
const filters: any[] = [];
|
||||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
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 isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||||
const partnerId = masterForm.partner_id;
|
const partnerId = masterForm.partner_id;
|
||||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
|||||||
if (isCustomerPrice && partnerId) {
|
if (isCustomerPrice && partnerId) {
|
||||||
try {
|
try {
|
||||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
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 }] },
|
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
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,
|
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
const resData = res.data?.data;
|
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) {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
setItemSearchResults(rows);
|
||||||
if (itemSearchDivision !== "all") {
|
setItemTotal(serverTotal);
|
||||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||||
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)));
|
|
||||||
} catch { /* skip */ } finally {
|
} catch { /* skip */ } finally {
|
||||||
setItemSearchLoading(false);
|
setItemSearchLoading(false);
|
||||||
}
|
}
|
||||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
|||||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||||
className="h-9 flex-1"
|
className="h-9 flex-1"
|
||||||
/>
|
/>
|
||||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
<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>
|
||||||
<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>
|
|
||||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
<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" />조회</>}
|
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Table style={{ minWidth: "900px" }}>
|
<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">
|
<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" />
|
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
<TableHead className="sticky left-[30px] z-20 bg-muted" 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 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 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-[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) => (
|
{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">
|
<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}
|
{col.label}
|
||||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
if (item._isGroupSummary) {
|
if (item._isGroupSummary) {
|
||||||
return (
|
return (
|
||||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||||
<TableCell colSpan={2} />
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||||
{ts.visibleColumns.map((col) => {
|
{ts.visibleColumns.map((col) => {
|
||||||
const v = (item as any)[col.key];
|
const v = (item as any)[col.key];
|
||||||
return (
|
return (
|
||||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={item.item_code || rowIdx}>
|
<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")}>
|
<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" />
|
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||||
</TableCell>
|
</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")} />
|
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</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 style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</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))}
|
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||||
<TableCell colSpan={2} className="pl-10">
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<span className="text-muted-foreground">수주번호:</span>
|
<span className="text-muted-foreground">수주번호:</span>
|
||||||
<span className="font-medium">{detail.order_no}</span>
|
<span className="font-medium">{detail.order_no}</span>
|
||||||
|
|||||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
|||||||
case "inspection_type": return (
|
case "inspection_type": return (
|
||||||
<TableCell key={col.key}>
|
<TableCell key={col.key}>
|
||||||
<div className="flex gap-1 flex-wrap">
|
<div className="flex gap-1 flex-wrap">
|
||||||
{group.types.map((t: string) => (
|
{group.types.map((t: string) => {
|
||||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||||
))}
|
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
|||||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{type}
|
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
"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"
|
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[] = [];
|
const filters: any[] = [];
|
||||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
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 isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||||
const partnerId = masterForm.partner_id;
|
const partnerId = masterForm.partner_id;
|
||||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
|||||||
if (isCustomerPrice && partnerId) {
|
if (isCustomerPrice && partnerId) {
|
||||||
try {
|
try {
|
||||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
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 }] },
|
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
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,
|
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
const resData = res.data?.data;
|
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) {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
setItemSearchResults(rows);
|
||||||
if (itemSearchDivision !== "all") {
|
setItemTotal(serverTotal);
|
||||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||||
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)));
|
|
||||||
} catch { /* skip */ } finally {
|
} catch { /* skip */ } finally {
|
||||||
setItemSearchLoading(false);
|
setItemSearchLoading(false);
|
||||||
}
|
}
|
||||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
|||||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||||
className="h-9 flex-1"
|
className="h-9 flex-1"
|
||||||
/>
|
/>
|
||||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
<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>
|
||||||
<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>
|
|
||||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
<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" />조회</>}
|
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Table style={{ minWidth: "900px" }}>
|
<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">
|
<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" />
|
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
<TableHead className="sticky left-[30px] z-20 bg-muted" 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 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 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-[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) => (
|
{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">
|
<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}
|
{col.label}
|
||||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
if (item._isGroupSummary) {
|
if (item._isGroupSummary) {
|
||||||
return (
|
return (
|
||||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||||
<TableCell colSpan={2} />
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||||
{ts.visibleColumns.map((col) => {
|
{ts.visibleColumns.map((col) => {
|
||||||
const v = (item as any)[col.key];
|
const v = (item as any)[col.key];
|
||||||
return (
|
return (
|
||||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={item.item_code || rowIdx}>
|
<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")}>
|
<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" />
|
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||||
</TableCell>
|
</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")} />
|
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</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 style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</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))}
|
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||||
<TableCell colSpan={2} className="pl-10">
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<span className="text-muted-foreground">수주번호:</span>
|
<span className="text-muted-foreground">수주번호:</span>
|
||||||
<span className="font-medium">{detail.order_no}</span>
|
<span className="font-medium">{detail.order_no}</span>
|
||||||
|
|||||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
|||||||
case "inspection_type": return (
|
case "inspection_type": return (
|
||||||
<TableCell key={col.key}>
|
<TableCell key={col.key}>
|
||||||
<div className="flex gap-1 flex-wrap">
|
<div className="flex gap-1 flex-wrap">
|
||||||
{group.types.map((t: string) => (
|
{group.types.map((t: string) => {
|
||||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||||
))}
|
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
|||||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{type}
|
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
"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"
|
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[] = [];
|
const filters: any[] = [];
|
||||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
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 isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||||
const partnerId = masterForm.partner_id;
|
const partnerId = masterForm.partner_id;
|
||||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
|||||||
if (isCustomerPrice && partnerId) {
|
if (isCustomerPrice && partnerId) {
|
||||||
try {
|
try {
|
||||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
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 }] },
|
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
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,
|
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
const resData = res.data?.data;
|
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) {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
setItemSearchResults(rows);
|
||||||
if (itemSearchDivision !== "all") {
|
setItemTotal(serverTotal);
|
||||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||||
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)));
|
|
||||||
} catch { /* skip */ } finally {
|
} catch { /* skip */ } finally {
|
||||||
setItemSearchLoading(false);
|
setItemSearchLoading(false);
|
||||||
}
|
}
|
||||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
|||||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||||
className="h-9 flex-1"
|
className="h-9 flex-1"
|
||||||
/>
|
/>
|
||||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
<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>
|
||||||
<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>
|
|
||||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
<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" />조회</>}
|
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Table style={{ minWidth: "900px" }}>
|
<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">
|
<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" />
|
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
<TableHead className="sticky left-[30px] z-20 bg-muted" 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 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 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-[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) => (
|
{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">
|
<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}
|
{col.label}
|
||||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
if (item._isGroupSummary) {
|
if (item._isGroupSummary) {
|
||||||
return (
|
return (
|
||||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||||
<TableCell colSpan={2} />
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||||
{ts.visibleColumns.map((col) => {
|
{ts.visibleColumns.map((col) => {
|
||||||
const v = (item as any)[col.key];
|
const v = (item as any)[col.key];
|
||||||
return (
|
return (
|
||||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={item.item_code || rowIdx}>
|
<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")}>
|
<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" />
|
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||||
</TableCell>
|
</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")} />
|
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</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 style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</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))}
|
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||||
<TableCell colSpan={2} className="pl-10">
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<span className="text-muted-foreground">수주번호:</span>
|
<span className="text-muted-foreground">수주번호:</span>
|
||||||
<span className="font-medium">{detail.order_no}</span>
|
<span className="font-medium">{detail.order_no}</span>
|
||||||
|
|||||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
|||||||
case "inspection_type": return (
|
case "inspection_type": return (
|
||||||
<TableCell key={col.key}>
|
<TableCell key={col.key}>
|
||||||
<div className="flex gap-1 flex-wrap">
|
<div className="flex gap-1 flex-wrap">
|
||||||
{group.types.map((t: string) => (
|
{group.types.map((t: string) => {
|
||||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||||
))}
|
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
|||||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{type}
|
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
"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"
|
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 (
|
return (
|
||||||
<Table style={{ minWidth: "900px" }}>
|
<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">
|
<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" />
|
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
<TableHead className="sticky left-[30px] z-20 bg-muted" 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 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 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-[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) => (
|
{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">
|
<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}
|
{col.label}
|
||||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
if (item._isGroupSummary) {
|
if (item._isGroupSummary) {
|
||||||
return (
|
return (
|
||||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||||
<TableCell colSpan={2} />
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||||
{ts.visibleColumns.map((col) => {
|
{ts.visibleColumns.map((col) => {
|
||||||
const v = (item as any)[col.key];
|
const v = (item as any)[col.key];
|
||||||
return (
|
return (
|
||||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={item.item_code || rowIdx}>
|
<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")}>
|
<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" />
|
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||||
</TableCell>
|
</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")} />
|
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</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 style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</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))}
|
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||||
<TableCell colSpan={2} className="pl-10">
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<span className="text-muted-foreground">수주번호:</span>
|
<span className="text-muted-foreground">수주번호:</span>
|
||||||
<span className="font-medium">{detail.order_no}</span>
|
<span className="font-medium">{detail.order_no}</span>
|
||||||
|
|||||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
|||||||
case "inspection_type": return (
|
case "inspection_type": return (
|
||||||
<TableCell key={col.key}>
|
<TableCell key={col.key}>
|
||||||
<div className="flex gap-1 flex-wrap">
|
<div className="flex gap-1 flex-wrap">
|
||||||
{group.types.map((t: string) => (
|
{group.types.map((t: string) => {
|
||||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||||
))}
|
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
|||||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{type}
|
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
"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"
|
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[] = [];
|
const filters: any[] = [];
|
||||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
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 isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||||
const partnerId = masterForm.partner_id;
|
const partnerId = masterForm.partner_id;
|
||||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
|||||||
if (isCustomerPrice && partnerId) {
|
if (isCustomerPrice && partnerId) {
|
||||||
try {
|
try {
|
||||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
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 }] },
|
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
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,
|
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
const resData = res.data?.data;
|
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) {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
setItemSearchResults(rows);
|
||||||
if (itemSearchDivision !== "all") {
|
setItemTotal(serverTotal);
|
||||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||||
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)));
|
|
||||||
} catch { /* skip */ } finally {
|
} catch { /* skip */ } finally {
|
||||||
setItemSearchLoading(false);
|
setItemSearchLoading(false);
|
||||||
}
|
}
|
||||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
|||||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||||
className="h-9 flex-1"
|
className="h-9 flex-1"
|
||||||
/>
|
/>
|
||||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
<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>
|
||||||
<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>
|
|
||||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
<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" />조회</>}
|
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Table style={{ minWidth: "900px" }}>
|
<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">
|
<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" />
|
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
<TableHead className="sticky left-[30px] z-20 bg-muted" 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 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 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-[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) => (
|
{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">
|
<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}
|
{col.label}
|
||||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
if (item._isGroupSummary) {
|
if (item._isGroupSummary) {
|
||||||
return (
|
return (
|
||||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||||
<TableCell colSpan={2} />
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||||
{ts.visibleColumns.map((col) => {
|
{ts.visibleColumns.map((col) => {
|
||||||
const v = (item as any)[col.key];
|
const v = (item as any)[col.key];
|
||||||
return (
|
return (
|
||||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={item.item_code || rowIdx}>
|
<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")}>
|
<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" />
|
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||||
</TableCell>
|
</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")} />
|
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</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 style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</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))}
|
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||||
<TableCell colSpan={2} className="pl-10">
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<span className="text-muted-foreground">수주번호:</span>
|
<span className="text-muted-foreground">수주번호:</span>
|
||||||
<span className="font-medium">{detail.order_no}</span>
|
<span className="font-medium">{detail.order_no}</span>
|
||||||
|
|||||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
|||||||
case "inspection_type": return (
|
case "inspection_type": return (
|
||||||
<TableCell key={col.key}>
|
<TableCell key={col.key}>
|
||||||
<div className="flex gap-1 flex-wrap">
|
<div className="flex gap-1 flex-wrap">
|
||||||
{group.types.map((t: string) => (
|
{group.types.map((t: string) => {
|
||||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||||
))}
|
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
|||||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{type}
|
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
"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"
|
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[] = [];
|
const filters: any[] = [];
|
||||||
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
|
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 isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI";
|
||||||
const partnerId = masterForm.partner_id;
|
const partnerId = masterForm.partner_id;
|
||||||
@@ -632,7 +641,7 @@ export default function SalesOrderPage() {
|
|||||||
if (isCustomerPrice && partnerId) {
|
if (isCustomerPrice && partnerId) {
|
||||||
try {
|
try {
|
||||||
const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
|
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 }] },
|
dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] },
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
@@ -642,31 +651,22 @@ export default function SalesOrderPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
|
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,
|
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
});
|
});
|
||||||
const resData = res.data?.data;
|
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) {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 관리품목 필터 (코드/라벨 혼재 대응)
|
setItemSearchResults(rows);
|
||||||
if (itemSearchDivision !== "all") {
|
setItemTotal(serverTotal);
|
||||||
const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || "";
|
setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s)));
|
||||||
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)));
|
|
||||||
} catch { /* skip */ } finally {
|
} catch { /* skip */ } finally {
|
||||||
setItemSearchLoading(false);
|
setItemSearchLoading(false);
|
||||||
}
|
}
|
||||||
@@ -1474,15 +1474,7 @@ export default function SalesOrderPage() {
|
|||||||
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
onKeyDown={(e) => e.key === "Enter" && triggerNewSearch()}
|
||||||
className="h-9 flex-1"
|
className="h-9 flex-1"
|
||||||
/>
|
/>
|
||||||
<Select value={itemSearchDivision} onValueChange={setItemSearchDivision}>
|
<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>
|
||||||
<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>
|
|
||||||
<Button size="sm" onClick={triggerNewSearch} disabled={itemSearchLoading} className="h-9">
|
<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" />조회</>}
|
{itemSearchLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <><Search className="w-4 h-4 mr-1" />조회</>}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1053,14 +1053,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Table style={{ minWidth: "900px" }}>
|
<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">
|
<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" />
|
<Checkbox checked={selectedItemGroups.size === orderItems.length && orderItems.length > 0} onCheckedChange={(c) => toggleAllItemGroups(!!c)} className="h-4 w-4" />
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead style={{ width: "40px", minWidth: "40px" }} />
|
<TableHead className="sticky left-[30px] z-20 bg-muted" 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 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 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-[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) => (
|
{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">
|
<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}
|
{col.label}
|
||||||
@@ -1073,9 +1073,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
if (item._isGroupSummary) {
|
if (item._isGroupSummary) {
|
||||||
return (
|
return (
|
||||||
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
<TableRow key={`summary-${rowIdx}`} className="bg-muted/60 font-semibold border-t border-primary/20">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-muted/60" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-muted/60" />
|
||||||
<TableCell colSpan={2} />
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-muted/60 border-r" />
|
||||||
{ts.visibleColumns.map((col) => {
|
{ts.visibleColumns.map((col) => {
|
||||||
const v = (item as any)[col.key];
|
const v = (item as any)[col.key];
|
||||||
return (
|
return (
|
||||||
@@ -1090,14 +1090,14 @@ export default function ProductionPlanManagementPage() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={item.item_code || rowIdx}>
|
<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")}>
|
<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" />
|
<Checkbox checked={selectedItemGroups.has(item.item_code)} onCheckedChange={() => toggleItemGroupSelect(item.item_code)} className="h-4 w-4" />
|
||||||
</TableCell>
|
</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")} />
|
<ChevronRight className={cn("h-4 w-4 transition-transform duration-200", expandedItems.has(item.item_code) && "rotate-90")} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_code}</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 style={{ width: "140px", minWidth: "140px" }} className="text-[13px] text-primary truncate" onClick={() => toggleItemExpand(item.item_code)}>{item.item_name}</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))}
|
{ts.visibleColumns.map((col) => renderGroupCell(col, item))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
{expandedItems.has(item.item_code) && item.orders?.map((detail: any) => {
|
||||||
@@ -1107,9 +1107,9 @@ export default function ProductionPlanManagementPage() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
<TableRow key={detail.id || detail.order_no} className="cursor-pointer hover:bg-muted/50">
|
||||||
<TableCell />
|
<TableCell className="sticky left-0 z-10 bg-card" />
|
||||||
<TableCell />
|
<TableCell className="sticky left-[30px] z-10 bg-card" />
|
||||||
<TableCell colSpan={2} className="pl-10">
|
<TableCell colSpan={2} className="sticky left-[70px] z-10 bg-card pl-10 border-r">
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<span className="text-muted-foreground">수주번호:</span>
|
<span className="text-muted-foreground">수주번호:</span>
|
||||||
<span className="font-medium">{detail.order_no}</span>
|
<span className="font-medium">{detail.order_no}</span>
|
||||||
|
|||||||
@@ -424,9 +424,10 @@ export default function ItemInspectionInfoPage() {
|
|||||||
case "inspection_type": return (
|
case "inspection_type": return (
|
||||||
<TableCell key={col.key}>
|
<TableCell key={col.key}>
|
||||||
<div className="flex gap-1 flex-wrap">
|
<div className="flex gap-1 flex-wrap">
|
||||||
{group.types.map((t: string) => (
|
{group.types.map((t: string) => {
|
||||||
<Badge key={t} variant="secondary" className="text-[10px]">{t}</Badge>
|
const label = inspTypeCatOptions.find((o) => o.code === t)?.label || t;
|
||||||
))}
|
return <Badge key={t} variant="secondary" className="text-[10px]">{label}</Badge>;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
@@ -494,7 +495,7 @@ export default function ItemInspectionInfoPage() {
|
|||||||
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
: "bg-muted/50 text-muted-foreground border-border hover:bg-muted"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{type}
|
{inspTypeCatOptions.find((o) => o.code === type)?.label || type}
|
||||||
<span className={cn(
|
<span className={cn(
|
||||||
"inline-flex items-center justify-center h-4 min-w-[16px] rounded-full text-[10px] font-bold px-1",
|
"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"
|
selectedTypeTab === type ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted-foreground/20 text-muted-foreground"
|
||||||
|
|||||||
Reference in New Issue
Block a user