설정들 고친거
This commit is contained in:
@@ -158,7 +158,7 @@ export function ListWidgetSection({ queryResult, config, onConfigChange }: ListW
|
||||
checked={popupConfig.additionalQuery?.enabled || false}
|
||||
onCheckedChange={(enabled) =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery, enabled, tableName: "", matchColumn: "" },
|
||||
additionalQuery: { ...popupConfig.additionalQuery, enabled, queryMode: "table", tableName: "", matchColumn: "" },
|
||||
})
|
||||
}
|
||||
aria-label="추가 데이터 조회 활성화"
|
||||
@@ -167,116 +167,230 @@ export function ListWidgetSection({ queryResult, config, onConfigChange }: ListW
|
||||
|
||||
{popupConfig.additionalQuery?.enabled && (
|
||||
<div className="space-y-2">
|
||||
{/* 조회 모드 선택 */}
|
||||
<div>
|
||||
<Label className="text-xs">테이블명</Label>
|
||||
<Input
|
||||
value={popupConfig.additionalQuery?.tableName || ""}
|
||||
onChange={(e) =>
|
||||
<Label className="text-xs">조회 모드</Label>
|
||||
<Select
|
||||
value={popupConfig.additionalQuery?.queryMode || "table"}
|
||||
onValueChange={(value: "table" | "custom") =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, tableName: e.target.value },
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, queryMode: value },
|
||||
})
|
||||
}
|
||||
placeholder="vehicles"
|
||||
className="mt-1 h-8 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs">매칭 컬럼 (조회 테이블)</Label>
|
||||
<Input
|
||||
value={popupConfig.additionalQuery?.matchColumn || ""}
|
||||
onChange={(e) =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, matchColumn: e.target.value },
|
||||
})
|
||||
}
|
||||
placeholder="id"
|
||||
className="mt-1 h-8 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs">소스 컬럼 (클릭한 행)</Label>
|
||||
<Input
|
||||
value={popupConfig.additionalQuery?.sourceColumn || ""}
|
||||
onChange={(e) =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, sourceColumn: e.target.value },
|
||||
})
|
||||
}
|
||||
placeholder="비워두면 매칭 컬럼과 동일"
|
||||
className="mt-1 h-8 text-xs"
|
||||
/>
|
||||
>
|
||||
<SelectTrigger className="mt-1 h-8 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="table">테이블 조회</SelectItem>
|
||||
<SelectItem value="custom">커스텀 쿼리</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 표시할 컬럼 선택 (다중 선택 + 라벨 편집) */}
|
||||
{/* 테이블 조회 모드 */}
|
||||
{(popupConfig.additionalQuery?.queryMode || "table") === "table" && (
|
||||
<>
|
||||
<div>
|
||||
<Label className="text-xs">테이블명</Label>
|
||||
<Input
|
||||
value={popupConfig.additionalQuery?.tableName || ""}
|
||||
onChange={(e) =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, tableName: e.target.value },
|
||||
})
|
||||
}
|
||||
placeholder="vehicles"
|
||||
className="mt-1 h-8 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs">매칭 컬럼 (조회 테이블)</Label>
|
||||
<Input
|
||||
value={popupConfig.additionalQuery?.matchColumn || ""}
|
||||
onChange={(e) =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, matchColumn: e.target.value },
|
||||
})
|
||||
}
|
||||
placeholder="id"
|
||||
className="mt-1 h-8 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs">소스 컬럼 (클릭한 행)</Label>
|
||||
<Input
|
||||
value={popupConfig.additionalQuery?.sourceColumn || ""}
|
||||
onChange={(e) =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, sourceColumn: e.target.value },
|
||||
})
|
||||
}
|
||||
placeholder="비워두면 매칭 컬럼과 동일"
|
||||
className="mt-1 h-8 text-xs"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 커스텀 쿼리 모드 */}
|
||||
{popupConfig.additionalQuery?.queryMode === "custom" && (
|
||||
<>
|
||||
<div>
|
||||
<Label className="text-xs">소스 컬럼 (클릭한 행)</Label>
|
||||
<Input
|
||||
value={popupConfig.additionalQuery?.sourceColumn || ""}
|
||||
onChange={(e) =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, sourceColumn: e.target.value },
|
||||
})
|
||||
}
|
||||
placeholder="id"
|
||||
className="mt-1 h-8 text-xs"
|
||||
/>
|
||||
<p className="text-muted-foreground mt-1 text-xs">쿼리에서 사용할 파라미터 컬럼</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs">커스텀 쿼리</Label>
|
||||
<textarea
|
||||
value={popupConfig.additionalQuery?.customQuery || ""}
|
||||
onChange={(e) =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, customQuery: e.target.value },
|
||||
})
|
||||
}
|
||||
placeholder={`SELECT
|
||||
v.vehicle_number AS "차량번호",
|
||||
ROUND(SUM(ts.loaded_distance_km)::NUMERIC, 2) AS "운행거리"
|
||||
FROM vehicles v
|
||||
LEFT JOIN transport_statistics ts ON v.id = ts.vehicle_id
|
||||
WHERE v.id = {id}
|
||||
GROUP BY v.id;`}
|
||||
className="mt-1 h-32 w-full rounded-md border border-input bg-background px-3 py-2 text-xs font-mono ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
/>
|
||||
<p className="text-muted-foreground mt-1 text-xs">
|
||||
{"{id}"}, {"{vehicle_number}"} 등 클릭한 행의 컬럼값을 파라미터로 사용
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 표시할 컬럼 선택 - 테이블 모드와 커스텀 쿼리 모드 분기 */}
|
||||
<div>
|
||||
<Label className="text-xs">표시할 컬럼 선택</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" className="mt-1 h-8 w-full justify-between text-xs">
|
||||
<span className="truncate">
|
||||
{(popupConfig.additionalQuery?.displayColumns?.length || 0) > 0
|
||||
? `${popupConfig.additionalQuery?.displayColumns?.length}개 선택됨`
|
||||
: "전체 표시 (클릭하여 선택)"}
|
||||
</span>
|
||||
<ChevronDown className="ml-2 h-3 w-3 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-72 p-2" align="start">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-xs font-medium">컬럼 선택</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 text-xs"
|
||||
onClick={() =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, displayColumns: [] },
|
||||
})
|
||||
}
|
||||
>
|
||||
초기화
|
||||
</Button>
|
||||
</div>
|
||||
<div className="max-h-48 space-y-1 overflow-y-auto">
|
||||
{/* 쿼리 결과 컬럼 목록 */}
|
||||
{queryResult?.columns.map((col) => {
|
||||
const currentColumns = popupConfig.additionalQuery?.displayColumns || [];
|
||||
const existingConfig = currentColumns.find((c) =>
|
||||
typeof c === 'object' ? c.column === col : c === col
|
||||
);
|
||||
const isSelected = !!existingConfig;
|
||||
return (
|
||||
<div
|
||||
key={col}
|
||||
className="flex cursor-pointer items-center gap-2 rounded px-2 py-1 hover:bg-muted"
|
||||
onClick={() => {
|
||||
const newColumns = isSelected
|
||||
? currentColumns.filter((c) =>
|
||||
typeof c === 'object' ? c.column !== col : c !== col
|
||||
)
|
||||
: [...currentColumns, { column: col, label: col } as DisplayColumnConfig];
|
||||
|
||||
{/* 테이블 모드: 기존 쿼리 결과에서 선택 */}
|
||||
{popupConfig.additionalQuery?.queryMode !== "custom" && (
|
||||
<>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" className="mt-1 h-8 w-full justify-between text-xs">
|
||||
<span className="truncate">
|
||||
{(popupConfig.additionalQuery?.displayColumns?.length || 0) > 0
|
||||
? `${popupConfig.additionalQuery?.displayColumns?.length}개 선택됨`
|
||||
: "전체 표시 (클릭하여 선택)"}
|
||||
</span>
|
||||
<ChevronDown className="ml-2 h-3 w-3 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-72 p-2" align="start">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-xs font-medium">컬럼 선택</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 text-xs"
|
||||
onClick={() =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, displayColumns: newColumns },
|
||||
});
|
||||
}}
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, displayColumns: [] },
|
||||
})
|
||||
}
|
||||
>
|
||||
<Checkbox checked={isSelected} className="h-3 w-3" />
|
||||
<span className="text-xs">{col}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{(!queryResult?.columns || queryResult.columns.length === 0) && (
|
||||
<p className="text-muted-foreground py-2 text-center text-xs">
|
||||
쿼리를 먼저 실행해주세요
|
||||
</p>
|
||||
초기화
|
||||
</Button>
|
||||
</div>
|
||||
<div className="max-h-48 space-y-1 overflow-y-auto">
|
||||
{/* 쿼리 결과 컬럼 목록 */}
|
||||
{queryResult?.columns.map((col) => {
|
||||
const currentColumns = popupConfig.additionalQuery?.displayColumns || [];
|
||||
const existingConfig = currentColumns.find((c) =>
|
||||
typeof c === 'object' ? c.column === col : c === col
|
||||
);
|
||||
const isSelected = !!existingConfig;
|
||||
return (
|
||||
<div
|
||||
key={col}
|
||||
className="flex cursor-pointer items-center gap-2 rounded px-2 py-1 hover:bg-muted"
|
||||
onClick={() => {
|
||||
const newColumns = isSelected
|
||||
? currentColumns.filter((c) =>
|
||||
typeof c === 'object' ? c.column !== col : c !== col
|
||||
)
|
||||
: [...currentColumns, { column: col, label: col } as DisplayColumnConfig];
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, displayColumns: newColumns },
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Checkbox checked={isSelected} className="h-3 w-3" />
|
||||
<span className="text-xs">{col}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{(!queryResult?.columns || queryResult.columns.length === 0) && (
|
||||
<p className="text-muted-foreground py-2 text-center text-xs">
|
||||
쿼리를 먼저 실행해주세요
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<p className="text-muted-foreground mt-1 text-xs">비워두면 모든 컬럼이 표시됩니다</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 커스텀 쿼리 모드: 직접 입력 방식 */}
|
||||
{popupConfig.additionalQuery?.queryMode === "custom" && (
|
||||
<>
|
||||
<p className="text-muted-foreground mt-1 text-xs">
|
||||
커스텀 쿼리의 결과 컬럼이 자동으로 표시됩니다.
|
||||
쿼리에서 AS "라벨명" 형태로 alias를 지정하면 해당 라벨로 표시됩니다.
|
||||
</p>
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 gap-1 text-xs"
|
||||
onClick={() => {
|
||||
const newColumns = [...(popupConfig.additionalQuery?.displayColumns || []), { column: "", label: "" }];
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, displayColumns: newColumns },
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
컬럼 추가 (선택사항)
|
||||
</Button>
|
||||
{(popupConfig.additionalQuery?.displayColumns?.length || 0) > 0 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
onClick={() =>
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, displayColumns: [] },
|
||||
})
|
||||
}
|
||||
>
|
||||
초기화
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<p className="text-muted-foreground mt-1 text-xs">비워두면 모든 컬럼이 표시됩니다</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 선택된 컬럼 라벨 편집 */}
|
||||
{(popupConfig.additionalQuery?.displayColumns?.length || 0) > 0 && (
|
||||
{/* 선택된 컬럼 라벨 편집 (테이블 모드) */}
|
||||
{popupConfig.additionalQuery?.queryMode !== "custom" && (popupConfig.additionalQuery?.displayColumns?.length || 0) > 0 && (
|
||||
<div className="mt-3 space-y-2">
|
||||
<Label className="text-xs">컬럼 라벨 설정</Label>
|
||||
<div className="space-y-1.5">
|
||||
@@ -321,6 +435,63 @@ export function ListWidgetSection({ queryResult, config, onConfigChange }: ListW
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 커스텀 쿼리 모드: 직접 입력 컬럼 편집 */}
|
||||
{popupConfig.additionalQuery?.queryMode === "custom" && (popupConfig.additionalQuery?.displayColumns?.length || 0) > 0 && (
|
||||
<div className="mt-3 space-y-2">
|
||||
<Label className="text-xs">표시할 컬럼 직접 입력</Label>
|
||||
<p className="text-muted-foreground text-xs">커스텀 쿼리 결과의 컬럼명을 직접 입력하세요</p>
|
||||
<div className="space-y-1.5">
|
||||
{popupConfig.additionalQuery?.displayColumns?.map((colConfig, index) => {
|
||||
const column = typeof colConfig === 'object' ? colConfig.column : colConfig;
|
||||
const label = typeof colConfig === 'object' ? colConfig.label : colConfig;
|
||||
return (
|
||||
<div key={index} className="flex items-center gap-2">
|
||||
<Input
|
||||
value={column}
|
||||
onChange={(e) => {
|
||||
const newColumns = [...(popupConfig.additionalQuery?.displayColumns || [])];
|
||||
newColumns[index] = { column: e.target.value, label: label || e.target.value };
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, displayColumns: newColumns },
|
||||
});
|
||||
}}
|
||||
placeholder="컬럼명 (쿼리 결과)"
|
||||
className="h-7 flex-1 text-xs"
|
||||
/>
|
||||
<Input
|
||||
value={label}
|
||||
onChange={(e) => {
|
||||
const newColumns = [...(popupConfig.additionalQuery?.displayColumns || [])];
|
||||
newColumns[index] = { column, label: e.target.value };
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, displayColumns: newColumns },
|
||||
});
|
||||
}}
|
||||
placeholder="표시 라벨"
|
||||
className="h-7 flex-1 text-xs"
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 w-7 p-0"
|
||||
onClick={() => {
|
||||
const newColumns = (popupConfig.additionalQuery?.displayColumns || []).filter(
|
||||
(_, i) => i !== index
|
||||
);
|
||||
updatePopupConfig({
|
||||
additionalQuery: { ...popupConfig.additionalQuery!, displayColumns: newColumns },
|
||||
});
|
||||
}}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user