refactor: Enhance unique constraint validation across data operations
- Integrated `TableManagementService` to validate unique constraints before insert, update, and upsert actions in various controllers, including `dataflowExecutionController`, `dynamicFormController`, and `tableManagementController`. - Improved error handling in `errorHandler` to provide detailed messages indicating which field has a unique constraint violation. - Updated the `formatPgError` utility to extract and display specific column labels for unique constraint violations, enhancing user feedback. - Adjusted the table schema retrieval to include company-specific nullable and unique constraints, ensuring accurate representation of database rules. These changes improve data integrity by preventing duplicate entries and enhance user experience through clearer error messages related to unique constraints.
This commit is contained in:
@@ -1061,19 +1061,22 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
// 백엔드 DISTINCT API로 전체 고유값 조회 (페이징과 무관하게 모든 값 반환)
|
||||
try {
|
||||
const { apiClient } = await import("@/lib/api/client");
|
||||
const response = await apiClient.get(`/entity/${tableConfig.selectedTable}/distinct/${columnName}`);
|
||||
// 카테고리/엔티티/코드 타입이 아닌 경우에만 DISTINCT API 사용
|
||||
// (카테고리 등은 DISTINCT로 조회하면 코드만 반환되어 label이 없음)
|
||||
if (!["category", "entity", "code"].includes(inputType)) {
|
||||
try {
|
||||
const { apiClient } = await import("@/lib/api/client");
|
||||
const response = await apiClient.get(`/entity/${tableConfig.selectedTable}/distinct/${columnName}`);
|
||||
|
||||
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
||||
return response.data.data.map((item: any) => ({
|
||||
value: String(item.value),
|
||||
label: String(item.label),
|
||||
}));
|
||||
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
||||
return response.data.data.map((item: any) => ({
|
||||
value: String(item.value),
|
||||
label: String(item.label),
|
||||
}));
|
||||
}
|
||||
} catch (error: any) {
|
||||
// DISTINCT API 실패 시 현재 데이터 기반으로 fallback
|
||||
}
|
||||
} catch (error: any) {
|
||||
// DISTINCT API 실패 시 현재 데이터 기반으로 fallback
|
||||
}
|
||||
|
||||
// fallback: 현재 로드된 데이터에서 고유 값 추출
|
||||
|
||||
@@ -467,14 +467,9 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
||||
|
||||
if (hasNewOptions) {
|
||||
setSelectOptions((prev) => {
|
||||
// 이미 로드된 옵션은 유지, 새로 로드된 옵션만 병합
|
||||
const merged = { ...prev };
|
||||
for (const [key, value] of Object.entries(loadedOptions)) {
|
||||
if (!merged[key] || merged[key].length === 0) {
|
||||
merged[key] = value;
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
// 새로 로드된 옵션으로 항상 갱신 (카테고리 label 정보가 나중에 로드될 수 있으므로)
|
||||
// 로드 실패한 컬럼의 기존 옵션은 유지
|
||||
return { ...prev, ...loadedOptions };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user