ERP-node/frontend/components/unified/UnifiedBiz.tsx

350 lines
10 KiB
TypeScript

"use client";
/**
* UnifiedBiz
*
* 통합 비즈니스 컴포넌트
* - flow: 플로우/워크플로우
* - rack: 랙 구조
* - map: 맵/위치
* - numbering: 채번 규칙
* - category: 카테고리 관리
* - mapping: 데이터 매핑
* - related-buttons: 관련 데이터 버튼
*/
import React, { forwardRef } from "react";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { UnifiedBizProps } from "@/types/unified-components";
import {
GitBranch,
LayoutGrid,
MapPin,
Hash,
FolderTree,
Link2,
FileText,
ArrowRight
} from "lucide-react";
/**
* 플로우 컴포넌트 (플레이스홀더)
* 실제 구현은 기존 FlowWidget과 연동
*/
const FlowBiz = forwardRef<HTMLDivElement, {
config?: Record<string, unknown>;
className?: string;
}>(({ config, className }, ref) => {
return (
<Card ref={ref} className={className}>
<CardHeader className="pb-3">
<CardTitle className="text-base flex items-center gap-2">
<GitBranch className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-48 border-2 border-dashed rounded-lg flex items-center justify-center text-muted-foreground">
<div className="text-center">
<GitBranch className="h-8 w-8 mx-auto mb-2" />
<p className="text-sm"> </p>
<p className="text-xs"> FlowWidget과 </p>
</div>
</div>
</CardContent>
</Card>
);
});
FlowBiz.displayName = "FlowBiz";
/**
* 랙 구조 컴포넌트 (플레이스홀더)
* 실제 구현은 기존 RackStructure와 연동
*/
const RackBiz = forwardRef<HTMLDivElement, {
config?: Record<string, unknown>;
className?: string;
}>(({ config, className }, ref) => {
return (
<Card ref={ref} className={className}>
<CardHeader className="pb-3">
<CardTitle className="text-base flex items-center gap-2">
<LayoutGrid className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-48 border-2 border-dashed rounded-lg flex items-center justify-center text-muted-foreground">
<div className="text-center">
<LayoutGrid className="h-8 w-8 mx-auto mb-2" />
<p className="text-sm"> </p>
<p className="text-xs"> RackStructure와 </p>
</div>
</div>
</CardContent>
</Card>
);
});
RackBiz.displayName = "RackBiz";
/**
* 맵 컴포넌트 (플레이스홀더)
*/
const MapBiz = forwardRef<HTMLDivElement, {
config?: Record<string, unknown>;
className?: string;
}>(({ config, className }, ref) => {
return (
<Card ref={ref} className={className}>
<CardHeader className="pb-3">
<CardTitle className="text-base flex items-center gap-2">
<MapPin className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-48 border-2 border-dashed rounded-lg flex items-center justify-center text-muted-foreground">
<div className="text-center">
<MapPin className="h-8 w-8 mx-auto mb-2" />
<p className="text-sm"> </p>
<p className="text-xs"> </p>
</div>
</div>
</CardContent>
</Card>
);
});
MapBiz.displayName = "MapBiz";
/**
* 채번 규칙 컴포넌트 (플레이스홀더)
* 실제 구현은 기존 NumberingRuleComponent와 연동
*/
const NumberingBiz = forwardRef<HTMLDivElement, {
config?: Record<string, unknown>;
className?: string;
}>(({ config, className }, ref) => {
return (
<Card ref={ref} className={className}>
<CardHeader className="pb-3">
<CardTitle className="text-base flex items-center gap-2">
<Hash className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center justify-between p-3 bg-muted/50 rounded-lg">
<div>
<p className="font-medium text-sm"> </p>
<p className="text-xs text-muted-foreground"> </p>
</div>
<div className="font-mono text-sm bg-background px-2 py-1 rounded border">
PO-2024-0001
</div>
</div>
<p className="text-xs text-muted-foreground text-center">
NumberingRuleComponent와
</p>
</div>
</CardContent>
</Card>
);
});
NumberingBiz.displayName = "NumberingBiz";
/**
* 카테고리 관리 컴포넌트 (플레이스홀더)
* 실제 구현은 기존 CategoryManager와 연동
*/
const CategoryBiz = forwardRef<HTMLDivElement, {
config?: Record<string, unknown>;
className?: string;
}>(({ config, className }, ref) => {
return (
<Card ref={ref} className={className}>
<CardHeader className="pb-3">
<CardTitle className="text-base flex items-center gap-2">
<FolderTree className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="pl-0 py-1 px-2 bg-muted/50 rounded">
<span className="text-sm"></span>
</div>
<div className="pl-4 py-1 px-2 text-sm text-muted-foreground">
</div>
<div className="pl-8 py-1 px-2 text-sm text-muted-foreground">
</div>
<p className="text-xs text-muted-foreground text-center mt-3">
CategoryManager와
</p>
</div>
</CardContent>
</Card>
);
});
CategoryBiz.displayName = "CategoryBiz";
/**
* 데이터 매핑 컴포넌트 (플레이스홀더)
*/
const MappingBiz = forwardRef<HTMLDivElement, {
config?: Record<string, unknown>;
className?: string;
}>(({ config, className }, ref) => {
return (
<Card ref={ref} className={className}>
<CardHeader className="pb-3">
<CardTitle className="text-base flex items-center gap-2">
<Link2 className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center gap-4 justify-center p-4">
<div className="text-center">
<div className="w-20 h-20 border-2 rounded-lg flex items-center justify-center mb-2">
<FileText className="h-6 w-6 text-muted-foreground" />
</div>
<p className="text-xs text-muted-foreground"></p>
</div>
<ArrowRight className="h-6 w-6 text-muted-foreground" />
<div className="text-center">
<div className="w-20 h-20 border-2 rounded-lg flex items-center justify-center mb-2">
<FileText className="h-6 w-6 text-muted-foreground" />
</div>
<p className="text-xs text-muted-foreground"></p>
</div>
</div>
</CardContent>
</Card>
);
});
MappingBiz.displayName = "MappingBiz";
/**
* 관련 데이터 버튼 컴포넌트 (플레이스홀더)
*/
const RelatedButtonsBiz = forwardRef<HTMLDivElement, {
config?: Record<string, unknown>;
className?: string;
}>(({ config, className }, ref) => {
const buttons = (config?.buttons as Array<{ label: string; icon?: string }>) || [
{ label: "관련 주문" },
{ label: "관련 출고" },
{ label: "이력 보기" },
];
return (
<div ref={ref} className={cn("flex flex-wrap gap-2", className)}>
{buttons.map((button, index) => (
<Button key={index} variant="outline" size="sm">
{button.label}
</Button>
))}
</div>
);
});
RelatedButtonsBiz.displayName = "RelatedButtonsBiz";
/**
* 메인 UnifiedBiz 컴포넌트
*/
export const UnifiedBiz = forwardRef<HTMLDivElement, UnifiedBizProps>(
(props, ref) => {
const {
id,
label,
style,
size,
config: configProp,
} = props;
// config가 없으면 기본값 사용
const config = configProp || { type: "flow" as const };
// 타입별 비즈니스 컴포넌트 렌더링
const renderBiz = () => {
const bizConfig = config.config || {};
const bizType = config.type || "flow";
switch (bizType) {
case "flow":
return <FlowBiz config={bizConfig} />;
case "rack":
return <RackBiz config={bizConfig} />;
case "map":
return <MapBiz config={bizConfig} />;
case "numbering":
return <NumberingBiz config={bizConfig} />;
case "category":
return <CategoryBiz config={bizConfig} />;
case "mapping":
return <MappingBiz config={bizConfig} />;
case "related-buttons":
return <RelatedButtonsBiz config={bizConfig} />;
default:
return (
<div className="p-4 border rounded text-center text-muted-foreground">
: {config.type}
</div>
);
}
};
const showLabel = label && style?.labelDisplay !== false;
const componentWidth = size?.width || style?.width;
const componentHeight = size?.height || style?.height;
return (
<div
ref={ref}
id={id}
className="flex flex-col"
style={{
width: componentWidth,
height: componentHeight,
}}
>
{showLabel && (
<Label
htmlFor={id}
style={{
fontSize: style?.labelFontSize,
color: style?.labelColor,
fontWeight: style?.labelFontWeight,
marginBottom: style?.labelMarginBottom,
}}
className="text-sm font-medium flex-shrink-0"
>
{label}
</Label>
)}
<div className="flex-1 min-h-0">
{renderBiz()}
</div>
</div>
);
}
);
UnifiedBiz.displayName = "UnifiedBiz";
export default UnifiedBiz;