[통계 프론트엔드] 검증 항목등록 수정에 컬럼명 선택 팝업 추가
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Search } from "lucide-react";
|
||||
|
||||
import { CodeSelect } from "@/entities/code/ui/code-select";
|
||||
import type { VerificationTargetManagementResponse } from "@/entities/statistics/model/verification-target-management-interface";
|
||||
import type {
|
||||
NumericColumnDto,
|
||||
VerificationTargetManagementResponse,
|
||||
} from "@/entities/statistics/model/verification-target-management-interface";
|
||||
import { verificationTargetManagementApi } from "@/entities/statistics/api/verification-target-management-service";
|
||||
import {
|
||||
type VerificationTargetManagementFormData,
|
||||
verificationTargetManagementSchema,
|
||||
@@ -23,6 +28,7 @@ import {
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
|
||||
@@ -45,7 +51,7 @@ export function VerificationTargetDialog({
|
||||
onSave,
|
||||
onDelete,
|
||||
}: VerificationTargetDialogProps) {
|
||||
const { control, handleSubmit, reset } =
|
||||
const { control, handleSubmit, reset, getValues, setValue } =
|
||||
useForm<VerificationTargetManagementFormData>({
|
||||
resolver: zodResolver(verificationTargetManagementSchema),
|
||||
defaultValues: data || {
|
||||
@@ -60,6 +66,11 @@ export function VerificationTargetDialog({
|
||||
},
|
||||
});
|
||||
|
||||
const [isColumnDialogOpen, setIsColumnDialogOpen] = useState(false);
|
||||
const [numericColumns, setNumericColumns] = useState<NumericColumnDto[]>(
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
reset(
|
||||
@@ -90,321 +101,459 @@ export function VerificationTargetDialog({
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSearchTableMetadata() {
|
||||
const statsCd = getValues("statsCd");
|
||||
if (!statsCd) {
|
||||
alert("통계항목을 선택해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const metadata =
|
||||
await verificationTargetManagementApi.getTableInfo(statsCd);
|
||||
|
||||
setValue("schmNm", metadata.schmNm, { shouldValidate: true });
|
||||
setValue("tblNm", metadata.tblNm, { shouldValidate: true });
|
||||
|
||||
if (metadata.pkColumns && metadata.pkColumns.length > 0) {
|
||||
setValue("pkColNm", metadata.pkColumns.join(", "), {
|
||||
shouldValidate: true,
|
||||
});
|
||||
}
|
||||
|
||||
setNumericColumns(metadata.numericColumns || []);
|
||||
setIsColumnDialogOpen(true);
|
||||
} catch (error) {
|
||||
alert("테이블 정보를 조회하는 중 오류가 발생했습니다.");
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelectColumn(colNm: string) {
|
||||
setValue("colNm", colNm, { shouldValidate: true });
|
||||
setIsColumnDialogOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-[800px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{data ? "검증 항목 상세" : "검증 항목 등록"}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-[800px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{data ? "검증 항목 상세" : "검증 항목 등록"}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Table variant="secondary">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableHead
|
||||
className="w-[150px] bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
통계항목
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="statsCd"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<CodeSelect
|
||||
upCd="STITEM"
|
||||
value={field.value}
|
||||
onValueChange={
|
||||
field.onChange
|
||||
}
|
||||
placeholder="선택"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableHead
|
||||
className="w-[150px] bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
검증명
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="vrfcTrgtNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
placeholder="검증명"
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Table variant="secondary">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableHead
|
||||
className="w-[150px] bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
통계항목
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
<Controller
|
||||
name="statsCd"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CodeSelect
|
||||
upCd="STITEM"
|
||||
value={field.value}
|
||||
onValueChange={
|
||||
field.onChange
|
||||
}
|
||||
placeholder="선택"
|
||||
className="flex-1"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableHead
|
||||
className="bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
스키마명
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="schmNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="스키마명"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableHead
|
||||
className="bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
테이블명
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="tblNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="테이블명"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableHead
|
||||
className="bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
컬럼명
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="colNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="컬럼명"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableHead
|
||||
className="bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
PK 키
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="pkColNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="PK 키"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
{tabValue !== "IMR" && (
|
||||
<TableRow>
|
||||
<TableHead className="bg-muted font-medium">
|
||||
중요도 계수(μ)
|
||||
<Button
|
||||
type="button"
|
||||
color="gray"
|
||||
variant="outline"
|
||||
onClick={
|
||||
handleSearchTableMetadata
|
||||
}
|
||||
className="px-3"
|
||||
>
|
||||
<Search className="mr-1 h-4 w-4" />
|
||||
조회
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableHead
|
||||
className="w-[150px] bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
검증명
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="ipcrCntVl"
|
||||
name="vrfcTrgtNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
placeholder="검증명"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableHead
|
||||
className="bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
스키마명
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="schmNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.1"
|
||||
value={
|
||||
field.value ?? ""
|
||||
}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value
|
||||
? Number(
|
||||
e
|
||||
.target
|
||||
.value,
|
||||
)
|
||||
: null,
|
||||
)
|
||||
}
|
||||
placeholder="(0 ~ 1, 0.1 단위)"
|
||||
{...field}
|
||||
placeholder="스키마명"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableHead className="bg-muted font-medium">
|
||||
백분위수 기준값(pct)
|
||||
<TableHead
|
||||
className="bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
테이블명
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="pctlCrtrVl"
|
||||
name="tblNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="0.5"
|
||||
step="0.1"
|
||||
value={
|
||||
field.value ?? ""
|
||||
}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value
|
||||
? Number(
|
||||
e
|
||||
.target
|
||||
.value,
|
||||
)
|
||||
: null,
|
||||
)
|
||||
}
|
||||
placeholder="(0 ~ 0.5, 0.1 단위)"
|
||||
{...field}
|
||||
placeholder="테이블명"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
{tabValue !== "IMR" && (
|
||||
<TableRow>
|
||||
<TableHead className="bg-muted font-medium">
|
||||
스케일 조정 계수(A)
|
||||
<TableHead
|
||||
className="bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
컬럼명
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="scopCntCntVl"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Controller
|
||||
name="colNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={
|
||||
field.value ?? ""
|
||||
}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value
|
||||
? Number(
|
||||
e
|
||||
.target
|
||||
.value,
|
||||
)
|
||||
: null,
|
||||
)
|
||||
}
|
||||
placeholder="(0 ~ 1, 0.01 단위)"
|
||||
{...field}
|
||||
placeholder="컬럼명"
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="whitespace-nowrap px-3"
|
||||
onClick={() =>
|
||||
setIsColumnDialogOpen(true)
|
||||
}
|
||||
>
|
||||
목록보기
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableHead className="bg-muted font-medium">
|
||||
임계 계수(c)
|
||||
<TableHead
|
||||
className="bg-muted font-medium"
|
||||
data-required
|
||||
>
|
||||
PK 키
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="thrsCntVl"
|
||||
name="pkColNm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
value={
|
||||
field.value ?? ""
|
||||
}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value
|
||||
? Number(
|
||||
e
|
||||
.target
|
||||
.value,
|
||||
)
|
||||
: null,
|
||||
)
|
||||
}
|
||||
placeholder="(0 ~ 100, 1 단위)"
|
||||
{...field}
|
||||
placeholder="PK 키"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<DialogFooter className="mt-6">
|
||||
<Button
|
||||
type="button"
|
||||
color="gray"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
취소
|
||||
</Button>
|
||||
{data && (
|
||||
{tabValue !== "IMR" && (
|
||||
<TableRow>
|
||||
<TableHead className="bg-muted font-medium">
|
||||
중요도 계수(μ)
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="ipcrCntVl"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.1"
|
||||
value={
|
||||
field.value ??
|
||||
""
|
||||
}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target
|
||||
.value
|
||||
? Number(
|
||||
e
|
||||
.target
|
||||
.value,
|
||||
)
|
||||
: null,
|
||||
)
|
||||
}
|
||||
placeholder="(0 ~ 1, 0.1 단위)"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableHead className="bg-muted font-medium">
|
||||
백분위수 기준값(pct)
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="pctlCrtrVl"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="0.5"
|
||||
step="0.1"
|
||||
value={
|
||||
field.value ??
|
||||
""
|
||||
}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target
|
||||
.value
|
||||
? Number(
|
||||
e
|
||||
.target
|
||||
.value,
|
||||
)
|
||||
: null,
|
||||
)
|
||||
}
|
||||
placeholder="(0 ~ 0.5, 0.1 단위)"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
{tabValue !== "IMR" && (
|
||||
<TableRow>
|
||||
<TableHead className="bg-muted font-medium">
|
||||
스케일 조정 계수(A)
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="scopCntCntVl"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={
|
||||
field.value ??
|
||||
""
|
||||
}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target
|
||||
.value
|
||||
? Number(
|
||||
e
|
||||
.target
|
||||
.value,
|
||||
)
|
||||
: null,
|
||||
)
|
||||
}
|
||||
placeholder="(0 ~ 1, 0.01 단위)"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableHead className="bg-muted font-medium">
|
||||
임계 계수(c)
|
||||
</TableHead>
|
||||
<TableCell>
|
||||
<Controller
|
||||
name="thrsCntVl"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
value={
|
||||
field.value ??
|
||||
""
|
||||
}
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target
|
||||
.value
|
||||
? Number(
|
||||
e
|
||||
.target
|
||||
.value,
|
||||
)
|
||||
: null,
|
||||
)
|
||||
}
|
||||
placeholder="(0 ~ 100, 1 단위)"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<DialogFooter className="mt-6">
|
||||
<Button
|
||||
type="button"
|
||||
color="red"
|
||||
onClick={handleDelete}
|
||||
color="gray"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
삭제
|
||||
취소
|
||||
</Button>
|
||||
)}
|
||||
<Button type="submit" color="green">
|
||||
저장
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
{data && (
|
||||
<Button
|
||||
type="button"
|
||||
color="red"
|
||||
onClick={handleDelete}
|
||||
>
|
||||
삭제
|
||||
</Button>
|
||||
)}
|
||||
<Button type="submit" color="green">
|
||||
저장
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<Dialog
|
||||
open={isColumnDialogOpen}
|
||||
onOpenChange={setIsColumnDialogOpen}
|
||||
>
|
||||
<DialogContent className="max-w-[600px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>숫자형 컬럼 목록</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="max-h-[400px] overflow-y-auto">
|
||||
<Table variant="default">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="bg-muted text-center font-bold">
|
||||
컬럼명
|
||||
</TableHead>
|
||||
<TableHead className="bg-muted text-center font-bold">
|
||||
데이터 타입
|
||||
</TableHead>
|
||||
<TableHead className="bg-muted text-center font-bold">
|
||||
코멘트
|
||||
</TableHead>
|
||||
<TableHead className="bg-muted text-center font-bold">
|
||||
선택
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{numericColumns.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={4}
|
||||
className="py-8 text-center text-gray-500"
|
||||
>
|
||||
조회된 숫자형 컬럼이 없습니다.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
numericColumns.map((col, idx) => (
|
||||
<TableRow key={idx}>
|
||||
<TableCell className="text-center">
|
||||
{col.columnName}
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
{col.dataType}
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
{col.columnComment}
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
handleSelectColumn(
|
||||
col.columnName,
|
||||
)
|
||||
}
|
||||
>
|
||||
입력
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
VerificationTargetManagementUpdateRequest,
|
||||
VerificationTargetManagementUpdateApiResponse,
|
||||
VerificationTargetManagementDeleteApiResponse,
|
||||
TableMetadataResponse,
|
||||
} from "../model/verification-target-management-interface";
|
||||
|
||||
export class VerificationTargetManagementApiService implements VerificationTargetManagementService {
|
||||
@@ -44,6 +45,18 @@ export class VerificationTargetManagementApiService implements VerificationTarge
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 메타데이터 조회
|
||||
* @param statsCd 통계항목 코드
|
||||
*/
|
||||
public async getTableInfo(statsCd: string): Promise<TableMetadataResponse> {
|
||||
const response = await client.get<TableMetadataResponse>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.VRFCTGT_MNG}/table-info`,
|
||||
{ params: { statsCd } },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 검증 대상 코드 목록 조회
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
import type { PageableList } from "@/shared/model";
|
||||
|
||||
export interface NumericColumnDto {
|
||||
columnName: string;
|
||||
dataType: string;
|
||||
columnComment: string;
|
||||
}
|
||||
|
||||
export interface TableMetadataResponse {
|
||||
schmNm: string;
|
||||
tblNm: string;
|
||||
pkColumns: string[];
|
||||
numericColumns: NumericColumnDto[];
|
||||
}
|
||||
import type { ApiResponse } from "./statistics-interface";
|
||||
|
||||
// 검증 대상 코드 응답 DTO
|
||||
|
||||
Reference in New Issue
Block a user