fix: Enhance error handling and validation messages in form data operations

- Integrated `formatPgError` utility to provide user-friendly error messages based on PostgreSQL error codes during form data operations.
- Updated error responses in `saveFormData`, `saveFormDataEnhanced`, `updateFormData`, and `updateFormDataPartial` to include specific messages based on the company context.
- Improved error handling in the frontend components to display relevant error messages from the server response, ensuring users receive clear feedback on save operations.
- Enhanced the required field validation by incorporating NOT NULL metadata checks across various components, improving the accuracy of form submissions.

These changes improve the overall user experience by providing clearer error messages and ensuring that required fields are properly validated based on both manual settings and database constraints.
This commit is contained in:
kjs
2026-03-10 14:47:05 +09:00
parent 43523a0bba
commit 28ef7e1226
16 changed files with 180 additions and 86 deletions

View File

@@ -42,6 +42,7 @@ import {
} from "./types";
import { defaultConfig, generateUniqueId } from "./config";
import { TableSectionRenderer } from "./TableSectionRenderer";
import { isColumnRequiredByMeta } from "@/lib/registry/DynamicComponentRenderer";
/**
* 🔗 연쇄 드롭다운 Select 필드 컴포넌트
@@ -1438,15 +1439,17 @@ export function UniversalFormModalComponent({
[linkedFieldDataCache],
);
// 필수 필드 검증
// 필수 필드 검증 (수동 required + NOT NULL 메타데이터 통합)
const mainTableName = config.saveConfig?.customApiSave?.multiTable?.mainTable?.tableName || config.saveConfig?.tableName;
const validateRequiredFields = useCallback((): { valid: boolean; missingFields: string[] } => {
const missingFields: string[] = [];
for (const section of config.sections) {
if (section.repeatable || section.type === "table") continue; // 반복 섹션 및 테이블 섹션은 별도 검증
if (section.repeatable || section.type === "table") continue;
for (const field of section.fields || []) {
if (field.required && !field.hidden && !field.numberingRule?.hidden) {
const isRequired = field.required || isColumnRequiredByMeta(mainTableName, field.columnName);
if (isRequired && !field.hidden && !field.numberingRule?.hidden) {
const value = formData[field.columnName];
if (value === undefined || value === null || value === "") {
missingFields.push(field.label || field.columnName);
@@ -1456,7 +1459,7 @@ export function UniversalFormModalComponent({
}
return { valid: missingFields.length === 0, missingFields };
}, [config.sections, formData]);
}, [config.sections, formData, mainTableName]);
// 다중 테이블 저장 (범용)
const saveWithMultiTable = useCallback(async () => {
@@ -2007,8 +2010,7 @@ export function UniversalFormModalComponent({
return (
<div key={fieldKey} className="space-y-1" style={{ gridColumn: `span ${actualGridSpan}` }}>
<Label htmlFor={fieldKey} className="text-sm font-medium">
{field.label}
{field.required && <span className="text-destructive ml-1">*</span>}
{field.label}{(field.required || isColumnRequiredByMeta(mainTableName, field.columnName)) && <span className="text-orange-500">*</span>}
{field.numberingRule?.enabled && <span className="text-muted-foreground ml-1 text-xs">()</span>}
</Label>
{fieldElement}