feat: POP 화면설정 채번규칙 셀렉트 박스 구현
- 새 type "numbering-rule" 추가 - NumberingRuleSelect 컴포넌트: 회사별 채번규칙 목록 자동 로드 - 입고/출고 설정에서 inbound/outbound 키워드로 필터링 - 등록된 채번규칙이 없으면 안내 메시지 표시
This commit is contained in:
@@ -165,15 +165,16 @@ interface SettingField {
|
||||
key: string;
|
||||
label: string;
|
||||
description: string;
|
||||
type: "toggle" | "text" | "number" | "select" | "color" | "tags" | "array-object";
|
||||
type: "toggle" | "text" | "number" | "select" | "color" | "tags" | "array-object" | "numbering-rule";
|
||||
defaultValue?: unknown;
|
||||
options?: { value: string; label: string }[];
|
||||
fields?: { key: string; label: string; type: string }[];
|
||||
tableFilter?: string; // numbering-rule용: inbound/outbound 등
|
||||
}
|
||||
|
||||
const SETTINGS_SCHEMA: Record<string, SettingField[]> = {
|
||||
inbound: [
|
||||
{ key: "numberingRuleId", label: "📋 입고번호 채번규칙", description: "입고 확정 시 사용할 채번규칙을 선택합니다", type: "text" },
|
||||
{ key: "numberingRuleId", label: "📋 입고번호 채번규칙", description: "입고 확정 시 사용할 채번규칙을 선택합니다", type: "numbering-rule", tableFilter: "inbound" },
|
||||
{ key: "barcodeEnabled", label: "바코드 스캔 (미구현)", description: "바코드/QR 스캔 기능을 사용합니다", type: "toggle" },
|
||||
{ key: "inspectionRequired", label: "검사 필수 (미구현)", description: "입고 시 검사 항목을 필수로 표시합니다", type: "toggle" },
|
||||
{ key: "photoUpload", label: "사진 첨부 (미구현)", description: "입고 확정 시 사진 첨부를 허용합니다", type: "toggle" },
|
||||
@@ -181,7 +182,7 @@ const SETTINGS_SCHEMA: Record<string, SettingField[]> = {
|
||||
{ key: "defectSeparation", label: "불량 분리 (미구현)", description: "양품/불량 수량을 분리 입력합니다", type: "toggle" },
|
||||
],
|
||||
outbound: [
|
||||
{ key: "numberingRuleId", label: "📋 출고번호 채번규칙", description: "출고 확정 시 사용할 채번규칙을 선택합니다", type: "text" },
|
||||
{ key: "numberingRuleId", label: "📋 출고번호 채번규칙", description: "출고 확정 시 사용할 채번규칙을 선택합니다", type: "numbering-rule", tableFilter: "outbound" },
|
||||
{ key: "barcodeEnabled", label: "바코드 스캔 (미구현)", description: "바코드/QR 스캔 기능을 사용합니다", type: "toggle" },
|
||||
{ key: "photoUpload", label: "사진 첨부 (미구현)", description: "출고 시 사진 첨부를 허용합니다", type: "toggle" },
|
||||
],
|
||||
@@ -256,6 +257,82 @@ const SETTINGS_SCHEMA: Record<string, SettingField[]> = {
|
||||
],
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// 채번규칙 셀렉트 컴포넌트
|
||||
// ============================================================
|
||||
|
||||
function NumberingRuleSelect({
|
||||
field,
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
field: SettingField;
|
||||
value: unknown;
|
||||
onChange: (value: unknown) => void;
|
||||
}) {
|
||||
const { user } = useAuth();
|
||||
const [rules, setRules] = useState<{ value: string; label: string }[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadRules = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const companyCode = user?.companyCode || "COMPANY_7";
|
||||
const res = await apiClient.get(`/numbering-rules?company_code=${companyCode}`);
|
||||
const data = res.data?.data || res.data || [];
|
||||
const allRules = Array.isArray(data) ? data : (data.rules || []);
|
||||
// tableFilter로 필터링 (inbound/outbound 등)
|
||||
const filtered = field.tableFilter
|
||||
? allRules.filter((r: any) =>
|
||||
(r.table_name || "").toLowerCase().includes(field.tableFilter!) ||
|
||||
(r.rule_name || r.column_name || "").toLowerCase().includes(field.tableFilter!)
|
||||
)
|
||||
: allRules;
|
||||
setRules(
|
||||
filtered.length > 0
|
||||
? filtered.map((r: any) => ({
|
||||
value: r.id || r.rule_id,
|
||||
label: `${r.rule_name || r.table_name + "." + r.column_name} (${r.prefix || ""}${r.separator || ""}...)`,
|
||||
}))
|
||||
: allRules.map((r: any) => ({
|
||||
value: r.id || r.rule_id,
|
||||
label: `${r.rule_name || r.table_name + "." + r.column_name}`,
|
||||
}))
|
||||
);
|
||||
} catch {
|
||||
setRules([]);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
loadRules();
|
||||
}, [user?.companyCode, field.tableFilter]);
|
||||
|
||||
return (
|
||||
<div className="py-3 border-b last:border-0 space-y-1.5">
|
||||
<Label className="text-sm font-medium">{field.label}</Label>
|
||||
<p className="text-xs text-muted-foreground">{field.description}</p>
|
||||
{loading ? (
|
||||
<p className="text-xs text-muted-foreground">채번규칙 로드 중...</p>
|
||||
) : rules.length === 0 ? (
|
||||
<p className="text-xs text-amber-600">등록된 채번규칙이 없습니다. PC에서 먼저 채번규칙을 등록해주세요.</p>
|
||||
) : (
|
||||
<Select value={(value as string) || ""} onValueChange={onChange}>
|
||||
<SelectTrigger className="w-full h-9">
|
||||
<SelectValue placeholder="채번규칙을 선택하세요" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none__">선택 안 함 (기본)</SelectItem>
|
||||
{rules.map((r) => (
|
||||
<SelectItem key={r.value} value={r.value}>{r.label}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Sub-components: TagEditor, ArrayObjectEditor
|
||||
// ============================================================
|
||||
@@ -484,6 +561,8 @@ function SettingRow({
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
case "numbering-rule":
|
||||
return <NumberingRuleSelect field={field} value={value} onChange={onChange} />;
|
||||
case "color":
|
||||
return (
|
||||
<div className="py-3 border-b last:border-0 space-y-1.5">
|
||||
|
||||
Reference in New Issue
Block a user