refactor: 코드 정리 및 불필요한 주석 제거
- EntityJoinController에서 중복 제거 설정 관련 주석 및 코드 삭제 - screenGroupController와 tableManagementController에서 AuthenticatedRequest 타입을 일반 Request로 변경 - 불필요한 로그 및 주석 제거로 코드 가독성 향상 - tableManagementController에서 에러 메시지 개선
This commit is contained in:
@@ -129,6 +129,7 @@ interface TableSettingModalProps {
|
||||
columns?: ColumnInfo[];
|
||||
filterColumns?: string[];
|
||||
onSaveSuccess?: () => void;
|
||||
isEmbedded?: boolean; // 탭 안에 임베드 모드로 표시
|
||||
}
|
||||
|
||||
// 검색 가능한 Select 컴포넌트
|
||||
@@ -256,6 +257,7 @@ export function TableSettingModal({
|
||||
columns = [],
|
||||
filterColumns = [],
|
||||
onSaveSuccess,
|
||||
isEmbedded = false,
|
||||
}: TableSettingModalProps) {
|
||||
const [activeTab, setActiveTab] = useState("columns");
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -304,9 +306,19 @@ export function TableSettingModal({
|
||||
// 초기 편집 상태 설정
|
||||
const initialEdits: Record<string, Partial<ColumnTypeInfo>> = {};
|
||||
columnsData.forEach((col) => {
|
||||
// referenceTable이 설정되어 있으면 inputType은 entity여야 함
|
||||
let effectiveInputType = col.inputType || "direct";
|
||||
if (col.referenceTable && effectiveInputType !== "entity") {
|
||||
effectiveInputType = "entity";
|
||||
}
|
||||
// codeCategory/codeValue가 설정되어 있으면 inputType은 code여야 함
|
||||
if (col.codeCategory && effectiveInputType !== "code") {
|
||||
effectiveInputType = "code";
|
||||
}
|
||||
|
||||
initialEdits[col.columnName] = {
|
||||
displayName: col.displayName,
|
||||
inputType: col.inputType || "direct",
|
||||
inputType: effectiveInputType,
|
||||
referenceTable: col.referenceTable,
|
||||
referenceColumn: col.referenceColumn,
|
||||
displayColumn: col.displayColumn,
|
||||
@@ -343,10 +355,10 @@ export function TableSettingModal({
|
||||
try {
|
||||
// 모든 화면 조회
|
||||
const screensResponse = await screenApi.getScreens({ size: 1000 });
|
||||
if (screensResponse.items) {
|
||||
if (screensResponse.data) {
|
||||
const usingScreens: ScreenUsingTable[] = [];
|
||||
|
||||
screensResponse.items.forEach((screen: any) => {
|
||||
screensResponse.data.forEach((screen: any) => {
|
||||
// 메인 테이블로 사용하는 경우
|
||||
if (screen.tableName === tableName) {
|
||||
usingScreens.push({
|
||||
@@ -418,6 +430,35 @@ export function TableSettingModal({
|
||||
},
|
||||
}));
|
||||
|
||||
// 입력 타입 변경 시 관련 필드 초기화
|
||||
if (field === "inputType") {
|
||||
// 엔티티가 아닌 다른 타입으로 변경하면 참조 설정 초기화
|
||||
if (value !== "entity") {
|
||||
setEditedColumns((prev) => ({
|
||||
...prev,
|
||||
[columnName]: {
|
||||
...prev[columnName],
|
||||
inputType: value,
|
||||
referenceTable: "",
|
||||
referenceColumn: "",
|
||||
displayColumn: "",
|
||||
},
|
||||
}));
|
||||
}
|
||||
// 코드가 아닌 다른 타입으로 변경하면 코드 설정 초기화
|
||||
if (value !== "code") {
|
||||
setEditedColumns((prev) => ({
|
||||
...prev,
|
||||
[columnName]: {
|
||||
...prev[columnName],
|
||||
inputType: value,
|
||||
codeCategory: "",
|
||||
codeValue: "",
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// 참조 테이블 변경 시 참조 컬럼 초기화
|
||||
if (field === "referenceTable") {
|
||||
setEditedColumns((prev) => ({
|
||||
@@ -452,8 +493,18 @@ export function TableSettingModal({
|
||||
|
||||
// detailSettings 처리 (Entity 타입인 경우)
|
||||
let finalDetailSettings = mergedColumn.detailSettings || "";
|
||||
|
||||
// referenceTable이 설정되어 있으면 inputType을 entity로 자동 설정
|
||||
let currentInputType = (mergedColumn.inputType || "") as string;
|
||||
if (mergedColumn.referenceTable && currentInputType !== "entity") {
|
||||
currentInputType = "entity";
|
||||
}
|
||||
// codeCategory가 설정되어 있으면 inputType을 code로 자동 설정
|
||||
if (mergedColumn.codeCategory && currentInputType !== "code") {
|
||||
currentInputType = "code";
|
||||
}
|
||||
|
||||
if (mergedColumn.inputType === "entity" && mergedColumn.referenceTable) {
|
||||
if (currentInputType === "entity" && mergedColumn.referenceTable) {
|
||||
// 기존 detailSettings를 파싱하거나 새로 생성
|
||||
let existingSettings: Record<string, unknown> = {};
|
||||
if (typeof mergedColumn.detailSettings === "string" && mergedColumn.detailSettings.trim().startsWith("{")) {
|
||||
@@ -479,7 +530,7 @@ export function TableSettingModal({
|
||||
}
|
||||
|
||||
// Code 타입인 경우 hierarchyRole을 detailSettings에 포함
|
||||
if (mergedColumn.inputType === "code" && (mergedColumn as any).hierarchyRole) {
|
||||
if (currentInputType === "code" && (mergedColumn as any).hierarchyRole) {
|
||||
let existingSettings: Record<string, unknown> = {};
|
||||
if (typeof finalDetailSettings === "string" && finalDetailSettings.trim().startsWith("{")) {
|
||||
try {
|
||||
@@ -502,7 +553,7 @@ export function TableSettingModal({
|
||||
const columnSetting: ColumnSettings = {
|
||||
columnName: columnName,
|
||||
columnLabel: mergedColumn.displayName || originalColumn.displayName || "",
|
||||
webType: mergedColumn.inputType || originalColumn.inputType || "text",
|
||||
inputType: currentInputType || "text", // referenceTable/codeCategory가 설정된 경우 자동 보정된 값 사용
|
||||
detailSettings: finalDetailSettings,
|
||||
codeCategory: mergedColumn.codeCategory || originalColumn.codeCategory || "",
|
||||
codeValue: mergedColumn.codeValue || originalColumn.codeValue || "",
|
||||
@@ -593,6 +644,158 @@ export function TableSettingModal({
|
||||
];
|
||||
};
|
||||
|
||||
// 임베드 모드
|
||||
if (isEmbedded) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-full flex-col">
|
||||
{/* 헤더 */}
|
||||
<div className="flex flex-shrink-0 items-center justify-between border-b pb-2 px-3 pt-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Table2 className="h-4 w-4 text-green-500" />
|
||||
<span className="text-sm font-medium">{tableLabel || tableName}</span>
|
||||
{tableName !== tableLabel && tableName !== (tableLabel || tableName) && (
|
||||
<span className="text-xs text-muted-foreground">({tableName})</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setShowTableManagementModal(true)}
|
||||
className="h-7 gap-1 text-xs"
|
||||
>
|
||||
<Settings className="h-3 w-3" />
|
||||
테이블 타입 관리
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleRefresh}
|
||||
className="h-7 w-7 p-0"
|
||||
disabled={loading}
|
||||
>
|
||||
<RefreshCw className={cn("h-3 w-3", loading && "animate-spin")} />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSaveAll}
|
||||
className="h-7 gap-1 text-xs"
|
||||
disabled={saving || loading}
|
||||
>
|
||||
{saving ? (
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
) : (
|
||||
<Save className="h-3 w-3" />
|
||||
)}
|
||||
저장
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-0 flex-1 gap-3 p-3">
|
||||
{/* 좌측: 탭 (40%) */}
|
||||
<div className="flex w-[40%] min-h-0 flex-col">
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={setActiveTab}
|
||||
className="flex min-h-0 flex-1 flex-col"
|
||||
>
|
||||
<TabsList className="h-8 flex-shrink-0">
|
||||
<TabsTrigger value="columns" className="gap-1 text-xs">
|
||||
<Columns3 className="h-3 w-3" />
|
||||
컬럼 설정
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="screens" className="gap-1 text-xs">
|
||||
<Monitor className="h-3 w-3" />
|
||||
화면 연동
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="references" className="gap-1 text-xs">
|
||||
<Eye className="h-3 w-3" />
|
||||
참조 관계
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="columns" className="mt-2 min-h-0 flex-1 overflow-hidden">
|
||||
<ColumnListTab
|
||||
columns={tableColumns.map((col) => ({
|
||||
...col,
|
||||
isPK: col.columnName === "id" || col.columnName.endsWith("_id"),
|
||||
isFK: (col.inputType as string) === "entity",
|
||||
}))}
|
||||
editedColumns={editedColumns}
|
||||
selectedColumn={selectedColumn}
|
||||
onSelectColumn={setSelectedColumn}
|
||||
loading={loading}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="screens" className="mt-2 min-h-0 flex-1 overflow-hidden">
|
||||
<ScreensTab screensUsingTable={screensUsingTable} loading={loading} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="references" className="mt-2 min-h-0 flex-1 overflow-hidden">
|
||||
<ReferenceTab
|
||||
tableName={tableName}
|
||||
tableLabel={tableLabel}
|
||||
referencedBy={referencedBy}
|
||||
joinColumnRefs={joinColumnRefs}
|
||||
loading={loading}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
{/* 우측: 상세 설정 (60%) */}
|
||||
<div className="flex w-[60%] min-h-0 flex-col rounded-lg border bg-muted/30 p-3">
|
||||
{selectedColumn && mergedColumns.find((c) => c.columnName === selectedColumn) ? (
|
||||
<ColumnDetailPanel
|
||||
columnInfo={mergedColumns.find((c) => c.columnName === selectedColumn)!}
|
||||
editedColumn={editedColumns[selectedColumn] || {}}
|
||||
tableOptions={tableOptions}
|
||||
inputTypeOptions={inputTypeOptions}
|
||||
getRefColumnOptions={getRefColumnOptions}
|
||||
loadingRefColumns={loadingRefColumns}
|
||||
onColumnChange={(field, value) => handleColumnChange(selectedColumn, field, value)}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full items-center justify-center text-sm text-muted-foreground">
|
||||
<div className="text-center">
|
||||
<Columns3 className="mx-auto h-12 w-12 text-muted-foreground/30" />
|
||||
<p className="mt-2">왼쪽에서 컬럼을 선택하면</p>
|
||||
<p>상세 설정을 할 수 있습니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 테이블 타입 관리 모달 */}
|
||||
<Dialog open={showTableManagementModal} onOpenChange={setShowTableManagementModal}>
|
||||
<DialogContent className="flex h-[90vh] max-h-[1000px] w-[95vw] max-w-[1400px] flex-col p-0">
|
||||
<div className="flex items-center justify-between border-b p-4">
|
||||
<h2 className="text-lg font-semibold">테이블 타입 관리</h2>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setShowTableManagementModal(false);
|
||||
loadTableData();
|
||||
}}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<TableManagementPage />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// 기존 모달 모드
|
||||
return (
|
||||
<>
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
@@ -843,6 +1046,7 @@ function ColumnListTab({
|
||||
<div className="space-y-1 px-3 pb-3">
|
||||
{filteredColumns.map((col) => {
|
||||
const edited = editedColumns[col.columnName] || {};
|
||||
// editedColumns에서 inputType을 가져옴 (초기화 시 이미 보정됨)
|
||||
const inputType = (edited.inputType || col.inputType || "text") as string;
|
||||
const isSelected = selectedColumn === col.columnName;
|
||||
|
||||
@@ -873,23 +1077,17 @@ function ColumnListTab({
|
||||
PK
|
||||
</Badge>
|
||||
)}
|
||||
{col.isFK && (
|
||||
<Badge variant="outline" className="bg-green-100 text-green-700 text-[10px] px-1.5">
|
||||
<Link2 className="mr-0.5 h-2.5 w-2.5" />
|
||||
FK
|
||||
</Badge>
|
||||
)}
|
||||
{(edited.referenceTable || col.referenceTable) && (
|
||||
{/* 엔티티 타입이거나 referenceTable이 설정되어 있으면 조인 배지 표시 (FK와 동일 의미) */}
|
||||
{(inputType === "entity" || edited.referenceTable || col.referenceTable) && (
|
||||
<Badge variant="outline" className="bg-blue-100 text-blue-700 text-[10px] px-1.5">
|
||||
<Link2 className="mr-0.5 h-2.5 w-2.5" />
|
||||
조인
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1 flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<div className="mt-1 text-xs text-muted-foreground">
|
||||
<span className="font-mono">{col.columnName}</span>
|
||||
<span>•</span>
|
||||
<span>{col.dataType}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -925,10 +1123,11 @@ function ColumnDetailPanel({
|
||||
onColumnChange,
|
||||
}: ColumnDetailPanelProps) {
|
||||
const currentLabel = editedColumn.displayName ?? columnInfo.displayName ?? "";
|
||||
const currentInputType = (editedColumn.inputType ?? columnInfo.inputType ?? "text") as string;
|
||||
const currentRefTable = editedColumn.referenceTable ?? columnInfo.referenceTable ?? "";
|
||||
const currentRefColumn = editedColumn.referenceColumn ?? columnInfo.referenceColumn ?? "";
|
||||
const currentDisplayColumn = editedColumn.displayColumn ?? columnInfo.displayColumn ?? "";
|
||||
// editedColumn에서 inputType을 가져옴 (초기화 시 이미 보정됨)
|
||||
const currentInputType = (editedColumn.inputType ?? columnInfo.inputType ?? "text") as string;
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
@@ -948,9 +1147,10 @@ function ColumnDetailPanel({
|
||||
Primary Key
|
||||
</Badge>
|
||||
)}
|
||||
{columnInfo.isFK && (
|
||||
<Badge variant="outline" className="bg-green-100 text-green-700 text-[10px]">
|
||||
Foreign Key
|
||||
{/* 엔티티 타입이거나 referenceTable이 있으면 조인 배지 표시 */}
|
||||
{(currentInputType === "entity" || currentRefTable) && (
|
||||
<Badge variant="outline" className="bg-blue-100 text-blue-700 text-[10px]">
|
||||
조인
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user