[agent-pipeline] pipe-20260311052455-y968 round-4
This commit is contained in:
parent
834c52a2b2
commit
f071777131
|
|
@ -1,75 +1,144 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ConfigPanelBuilderProps } from "./ConfigPanelTypes";
|
import { ConfigPanelBuilderProps, ConfigSectionDefinition } from "./ConfigPanelTypes";
|
||||||
import { ConfigSection } from "./ConfigSection";
|
import { ConfigSection } from "./ConfigSection";
|
||||||
import { ConfigField } from "./ConfigField";
|
import { ConfigField } from "./ConfigField";
|
||||||
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
||||||
|
|
||||||
|
function renderSections<T extends Record<string, any>>(
|
||||||
|
sections: ConfigSectionDefinition<T>[],
|
||||||
|
config: T,
|
||||||
|
onChange: (key: string, value: any) => void,
|
||||||
|
tableColumns?: any[],
|
||||||
|
) {
|
||||||
|
return sections.map((section) => {
|
||||||
|
if (section.condition && !section.condition(config)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const visibleFields = section.fields.filter(
|
||||||
|
(field) => !field.condition || field.condition(config),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (visibleFields.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfigSection key={section.id} section={section}>
|
||||||
|
{visibleFields.map((field) => (
|
||||||
|
<ConfigField
|
||||||
|
key={field.key}
|
||||||
|
field={field}
|
||||||
|
value={(config as any)[field.key]}
|
||||||
|
onChange={onChange}
|
||||||
|
tableColumns={tableColumns}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ConfigSection>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function ConfigPanelBuilder<T extends Record<string, any>>({
|
export function ConfigPanelBuilder<T extends Record<string, any>>({
|
||||||
config,
|
config,
|
||||||
onChange,
|
onChange,
|
||||||
|
onConfigChange,
|
||||||
sections,
|
sections,
|
||||||
presets,
|
presets,
|
||||||
tableColumns,
|
tableColumns,
|
||||||
children,
|
children,
|
||||||
|
mode = "flat",
|
||||||
|
context,
|
||||||
}: ConfigPanelBuilderProps<T>) {
|
}: ConfigPanelBuilderProps<T>) {
|
||||||
return (
|
const effectiveTableColumns = tableColumns || context?.tableColumns;
|
||||||
<div className="space-y-3">
|
|
||||||
{/* 프리셋 버튼 */}
|
const presetSection = presets && presets.length > 0 && (
|
||||||
{presets && presets.length > 0 && (
|
<div className="border-b border-border/40 pb-2.5">
|
||||||
<div className="border-b pb-3">
|
<h4 className="mb-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
|
||||||
<h4 className="mb-2 text-xs font-medium text-muted-foreground">
|
빠른 설정
|
||||||
빠른 설정
|
</h4>
|
||||||
</h4>
|
<div className="flex flex-wrap gap-1">
|
||||||
<div className="flex flex-wrap gap-1">
|
{presets.map((preset, idx) => (
|
||||||
{presets.map((preset, idx) => (
|
<button
|
||||||
<button
|
key={idx}
|
||||||
key={idx}
|
onClick={() => {
|
||||||
onClick={() => {
|
Object.entries(preset.values).forEach(([key, value]) => {
|
||||||
Object.entries(preset.values).forEach(([key, value]) => {
|
onChange(key, value);
|
||||||
onChange(key, value);
|
});
|
||||||
});
|
if (onConfigChange) {
|
||||||
}}
|
onConfigChange({ ...config, ...preset.values } as Record<string, any>);
|
||||||
className="rounded-full bg-muted px-2.5 py-1 text-[10px] font-medium text-muted-foreground transition-colors hover:bg-primary hover:text-primary-foreground"
|
}
|
||||||
title={preset.description}
|
}}
|
||||||
>
|
className="rounded border border-border bg-background px-2 py-0.5 text-[10px] font-medium text-muted-foreground transition-colors hover:border-primary hover:text-primary"
|
||||||
{preset.label}
|
title={preset.description}
|
||||||
</button>
|
>
|
||||||
))}
|
{preset.label}
|
||||||
</div>
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mode === "tabs") {
|
||||||
|
const groupMap = new Map<string, ConfigSectionDefinition<T>[]>();
|
||||||
|
const ungrouped: ConfigSectionDefinition<T>[] = [];
|
||||||
|
|
||||||
|
for (const section of sections) {
|
||||||
|
if (section.group) {
|
||||||
|
const existing = groupMap.get(section.group) || [];
|
||||||
|
existing.push(section);
|
||||||
|
groupMap.set(section.group, existing);
|
||||||
|
} else {
|
||||||
|
ungrouped.push(section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabGroups = Array.from(groupMap.entries());
|
||||||
|
|
||||||
|
if (tabGroups.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-1">
|
||||||
|
{presetSection}
|
||||||
|
{renderSections(sections, config, onChange, effectiveTableColumns)}
|
||||||
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* 섹션 렌더링 */}
|
const defaultTab = tabGroups[0]?.[0] || "general";
|
||||||
{sections.map((section) => {
|
|
||||||
if (section.condition && !section.condition(config)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const visibleFields = section.fields.filter(
|
return (
|
||||||
(field) => !field.condition || field.condition(config),
|
<div className="space-y-1">
|
||||||
);
|
{presetSection}
|
||||||
|
|
||||||
if (visibleFields.length === 0) {
|
{ungrouped.length > 0 && renderSections(ungrouped, config, onChange, effectiveTableColumns)}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
<Tabs defaultValue={defaultTab} className="w-full">
|
||||||
<ConfigSection key={section.id} section={section}>
|
<TabsList className="h-7 w-full">
|
||||||
{visibleFields.map((field) => (
|
{tabGroups.map(([groupName]) => (
|
||||||
<ConfigField
|
<TabsTrigger key={groupName} value={groupName} className="h-6 text-xs">
|
||||||
key={field.key}
|
{groupName}
|
||||||
field={field}
|
</TabsTrigger>
|
||||||
value={(config as any)[field.key]}
|
|
||||||
onChange={onChange}
|
|
||||||
tableColumns={tableColumns}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</ConfigSection>
|
</TabsList>
|
||||||
);
|
{tabGroups.map(([groupName, groupSections]) => (
|
||||||
})}
|
<TabsContent key={groupName} value={groupName} className="mt-1">
|
||||||
|
{renderSections(groupSections, config, onChange, effectiveTableColumns)}
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
{/* 커스텀 children */}
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-1">
|
||||||
|
{presetSection}
|
||||||
|
{renderSections(sections, config, onChange, effectiveTableColumns)}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ export interface ConfigSectionDefinition<T = any> {
|
||||||
export interface ConfigPanelBuilderProps<T = any> {
|
export interface ConfigPanelBuilderProps<T = any> {
|
||||||
config: T;
|
config: T;
|
||||||
onChange: (key: string, value: any) => void;
|
onChange: (key: string, value: any) => void;
|
||||||
|
onConfigChange?: (config: Record<string, any>) => void;
|
||||||
sections: ConfigSectionDefinition<T>[];
|
sections: ConfigSectionDefinition<T>[];
|
||||||
presets?: Array<{
|
presets?: Array<{
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -56,6 +57,8 @@ export interface ConfigPanelBuilderProps<T = any> {
|
||||||
}>;
|
}>;
|
||||||
tableColumns?: ConfigOption[];
|
tableColumns?: ConfigOption[];
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
|
mode?: "flat" | "tabs";
|
||||||
|
context?: ConfigPanelContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue