refactor: 코드 정리 및 가독성 향상
- numberingRuleController.ts에서 API 엔드포인트의 코드 스타일을 일관되게 정리하여 가독성을 높였습니다. - 불필요한 줄바꿈을 제거하고, 코드 블록을 명확하게 정리하여 유지보수성을 개선했습니다. - tableManagementService.ts와 ButtonConfigPanel.tsx에서 코드 정리를 통해 일관성을 유지하고, 가독성을 향상시켰습니다. - 전반적으로 코드의 깔끔함을 유지하고, 향후 개발 시 이해하기 쉽게 개선했습니다.
This commit is contained in:
@@ -26,13 +26,9 @@ export async function getNumberingRules(): Promise<ApiResponse<NumberingRuleConf
|
||||
* @param menuObjid 현재 메뉴의 objid (선택)
|
||||
* @returns 사용 가능한 채번 규칙 목록
|
||||
*/
|
||||
export async function getAvailableNumberingRules(
|
||||
menuObjid?: number
|
||||
): Promise<ApiResponse<NumberingRuleConfig[]>> {
|
||||
export async function getAvailableNumberingRules(menuObjid?: number): Promise<ApiResponse<NumberingRuleConfig[]>> {
|
||||
try {
|
||||
const url = menuObjid
|
||||
? `/numbering-rules/available/${menuObjid}`
|
||||
: "/numbering-rules/available";
|
||||
const url = menuObjid ? `/numbering-rules/available/${menuObjid}` : "/numbering-rules/available";
|
||||
const response = await apiClient.get(url);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
@@ -46,7 +42,7 @@ export async function getAvailableNumberingRules(
|
||||
* @returns 해당 테이블의 채번 규칙 목록
|
||||
*/
|
||||
export async function getAvailableNumberingRulesForScreen(
|
||||
tableName: string
|
||||
tableName: string,
|
||||
): Promise<ApiResponse<NumberingRuleConfig[]>> {
|
||||
try {
|
||||
const response = await apiClient.get("/numbering-rules/available-for-screen", {
|
||||
@@ -70,9 +66,7 @@ export async function getNumberingRuleById(ruleId: string): Promise<ApiResponse<
|
||||
}
|
||||
}
|
||||
|
||||
export async function createNumberingRule(
|
||||
config: NumberingRuleConfig
|
||||
): Promise<ApiResponse<NumberingRuleConfig>> {
|
||||
export async function createNumberingRule(config: NumberingRuleConfig): Promise<ApiResponse<NumberingRuleConfig>> {
|
||||
try {
|
||||
const response = await apiClient.post("/numbering-rules", config);
|
||||
return response.data;
|
||||
@@ -83,7 +77,7 @@ export async function createNumberingRule(
|
||||
|
||||
export async function updateNumberingRule(
|
||||
ruleId: string,
|
||||
config: Partial<NumberingRuleConfig>
|
||||
config: Partial<NumberingRuleConfig>,
|
||||
): Promise<ApiResponse<NumberingRuleConfig>> {
|
||||
try {
|
||||
const response = await apiClient.put(`/numbering-rules/${ruleId}`, config);
|
||||
@@ -110,7 +104,7 @@ export async function deleteNumberingRule(ruleId: string): Promise<ApiResponse<v
|
||||
*/
|
||||
export async function previewNumberingCode(
|
||||
ruleId: string,
|
||||
formData?: Record<string, unknown>
|
||||
formData?: Record<string, unknown>,
|
||||
): Promise<ApiResponse<{ generatedCode: string }>> {
|
||||
// ruleId 유효성 검사
|
||||
if (!ruleId || ruleId === "undefined" || ruleId === "null") {
|
||||
@@ -127,11 +121,8 @@ export async function previewNumberingCode(
|
||||
return response.data;
|
||||
} catch (error: unknown) {
|
||||
const err = error as { response?: { data?: { error?: string; message?: string } }; message?: string };
|
||||
const errorMessage =
|
||||
err.response?.data?.error ||
|
||||
err.response?.data?.message ||
|
||||
err.message ||
|
||||
"코드 미리보기 실패";
|
||||
const errorMessage =
|
||||
err.response?.data?.error || err.response?.data?.message || err.message || "코드 미리보기 실패";
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
}
|
||||
@@ -146,7 +137,7 @@ export async function previewNumberingCode(
|
||||
export async function allocateNumberingCode(
|
||||
ruleId: string,
|
||||
userInputCode?: string,
|
||||
formData?: Record<string, any>
|
||||
formData?: Record<string, any>,
|
||||
): Promise<ApiResponse<{ generatedCode: string }>> {
|
||||
try {
|
||||
const response = await apiClient.post(`/numbering-rules/${ruleId}/allocate`, {
|
||||
@@ -162,9 +153,7 @@ export async function allocateNumberingCode(
|
||||
/**
|
||||
* @deprecated 기존 generateNumberingCode는 previewNumberingCode를 사용하세요
|
||||
*/
|
||||
export async function generateNumberingCode(
|
||||
ruleId: string
|
||||
): Promise<ApiResponse<{ generatedCode: string }>> {
|
||||
export async function generateNumberingCode(ruleId: string): Promise<ApiResponse<{ generatedCode: string }>> {
|
||||
console.warn("generateNumberingCode는 deprecated. previewNumberingCode 사용 권장");
|
||||
return previewNumberingCode(ruleId);
|
||||
}
|
||||
@@ -188,13 +177,9 @@ export async function resetSequence(ruleId: string): Promise<ApiResponse<void>>
|
||||
* numbering_rules 테이블 사용
|
||||
* @param menuObjid 메뉴 OBJID (선택) - 필터링용
|
||||
*/
|
||||
export async function getNumberingRulesFromTest(
|
||||
menuObjid?: number
|
||||
): Promise<ApiResponse<NumberingRuleConfig[]>> {
|
||||
export async function getNumberingRulesFromTest(menuObjid?: number): Promise<ApiResponse<NumberingRuleConfig[]>> {
|
||||
try {
|
||||
const url = menuObjid
|
||||
? `/numbering-rules/test/list/${menuObjid}`
|
||||
: "/numbering-rules/test/list";
|
||||
const url = menuObjid ? `/numbering-rules/test/list/${menuObjid}` : "/numbering-rules/test/list";
|
||||
const response = await apiClient.get(url);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
@@ -211,7 +196,7 @@ export async function getNumberingRulesFromTest(
|
||||
*/
|
||||
export async function getNumberingRuleByColumn(
|
||||
tableName: string,
|
||||
columnName: string
|
||||
columnName: string,
|
||||
): Promise<ApiResponse<NumberingRuleConfig>> {
|
||||
try {
|
||||
const response = await apiClient.get("/numbering-rules/test/by-column", {
|
||||
@@ -230,9 +215,7 @@ export async function getNumberingRuleByColumn(
|
||||
* [테스트] 테스트 테이블에 채번규칙 저장
|
||||
* numbering_rules 테이블 사용
|
||||
*/
|
||||
export async function saveNumberingRuleToTest(
|
||||
config: NumberingRuleConfig
|
||||
): Promise<ApiResponse<NumberingRuleConfig>> {
|
||||
export async function saveNumberingRuleToTest(config: NumberingRuleConfig): Promise<ApiResponse<NumberingRuleConfig>> {
|
||||
try {
|
||||
const response = await apiClient.post("/numbering-rules/test/save", config);
|
||||
return response.data;
|
||||
@@ -248,9 +231,7 @@ export async function saveNumberingRuleToTest(
|
||||
* [테스트] 테스트 테이블에서 채번규칙 삭제
|
||||
* numbering_rules 테이블 사용
|
||||
*/
|
||||
export async function deleteNumberingRuleFromTest(
|
||||
ruleId: string
|
||||
): Promise<ApiResponse<void>> {
|
||||
export async function deleteNumberingRuleFromTest(ruleId: string): Promise<ApiResponse<void>> {
|
||||
try {
|
||||
const response = await apiClient.delete(`/numbering-rules/test/${ruleId}`);
|
||||
return response.data;
|
||||
@@ -270,7 +251,7 @@ export async function getNumberingRuleByColumnWithCategory(
|
||||
tableName: string,
|
||||
columnName: string,
|
||||
categoryColumn?: string,
|
||||
categoryValueId?: number
|
||||
categoryValueId?: number,
|
||||
): Promise<ApiResponse<NumberingRuleConfig>> {
|
||||
try {
|
||||
const response = await apiClient.get("/numbering-rules/test/by-column-with-category", {
|
||||
@@ -290,7 +271,7 @@ export async function getNumberingRuleByColumnWithCategory(
|
||||
*/
|
||||
export async function getRulesByTableColumn(
|
||||
tableName: string,
|
||||
columnName: string
|
||||
columnName: string,
|
||||
): Promise<ApiResponse<NumberingRuleConfig[]>> {
|
||||
try {
|
||||
const response = await apiClient.get("/numbering-rules/test/rules-by-table-column", {
|
||||
@@ -304,4 +285,3 @@ export async function getRulesByTableColumn(
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -115,14 +115,14 @@ const CascadingSelectField: React.FC<CascadingSelectFieldProps> = ({
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="absolute right-0 top-0 h-full px-2 hover:bg-transparent"
|
||||
className="absolute top-0 right-0 h-full px-2 hover:bg-transparent"
|
||||
onClick={() => !isDisabled && setOpen(!open)}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
|
||||
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<ChevronsUpDown className="h-4 w-4 text-muted-foreground" />
|
||||
<ChevronsUpDown className="text-muted-foreground h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -149,12 +149,7 @@ const CascadingSelectField: React.FC<CascadingSelectFieldProps> = ({
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
value === option.value ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<Check className={cn("mr-2 h-4 w-4", value === option.value ? "opacity-100" : "opacity-0")} />
|
||||
{option.label}
|
||||
</CommandItem>
|
||||
))}
|
||||
@@ -437,19 +432,19 @@ export function UniversalFormModalComponent({
|
||||
}
|
||||
|
||||
// 🆕 테이블 섹션 데이터 병합 (품목 리스트 등)
|
||||
// 참고: initializeForm에서 DB 로드 시 __tableSection_ (더블),
|
||||
// 참고: initializeForm에서 DB 로드 시 __tableSection_ (더블),
|
||||
// handleTableDataChange에서 수정 시 _tableSection_ (싱글) 사용
|
||||
for (const [key, value] of Object.entries(formData)) {
|
||||
// 싱글/더블 언더스코어 모두 처리
|
||||
if ((key.startsWith("_tableSection_") || key.startsWith("__tableSection_")) && Array.isArray(value)) {
|
||||
// 저장 시에는 _tableSection_ 키로 통일 (buttonActions.ts에서 이 키를 기대)
|
||||
const normalizedKey = key.startsWith("__tableSection_")
|
||||
? key.replace("__tableSection_", "_tableSection_")
|
||||
const normalizedKey = key.startsWith("__tableSection_")
|
||||
? key.replace("__tableSection_", "_tableSection_")
|
||||
: key;
|
||||
event.detail.formData[normalizedKey] = value;
|
||||
console.log(`[UniversalFormModal] 테이블 섹션 병합: ${key} → ${normalizedKey}, ${value.length}개 항목`);
|
||||
}
|
||||
|
||||
|
||||
// 🆕 원본 테이블 섹션 데이터도 병합 (삭제 추적용)
|
||||
if (key.startsWith("_originalTableSectionData_") && Array.isArray(value)) {
|
||||
event.detail.formData[key] = value;
|
||||
@@ -948,13 +943,17 @@ export function UniversalFormModalComponent({
|
||||
// 각 테이블 섹션별로 별도의 키에 원본 데이터 저장 (groupedDataInitializedRef와 무관하게 항상 저장)
|
||||
const originalTableSectionKey = `_originalTableSectionData_${section.id}`;
|
||||
newFormData[originalTableSectionKey] = JSON.parse(JSON.stringify(items));
|
||||
console.log(`[initializeForm] 테이블 섹션 ${section.id}: formData[${originalTableSectionKey}]에 원본 ${items.length}건 저장`);
|
||||
|
||||
console.log(
|
||||
`[initializeForm] 테이블 섹션 ${section.id}: formData[${originalTableSectionKey}]에 원본 ${items.length}건 저장`,
|
||||
);
|
||||
|
||||
// 기존 originalGroupedData에도 추가 (하위 호환성)
|
||||
if (!groupedDataInitializedRef.current) {
|
||||
setOriginalGroupedData((prev) => {
|
||||
const newOriginal = [...prev, ...JSON.parse(JSON.stringify(items))];
|
||||
console.log(`[initializeForm] 테이블 섹션 ${section.id}: originalGroupedData에 ${items.length}건 추가 (총 ${newOriginal.length}건)`);
|
||||
console.log(
|
||||
`[initializeForm] 테이블 섹션 ${section.id}: originalGroupedData에 ${items.length}건 추가 (총 ${newOriginal.length}건)`,
|
||||
);
|
||||
return newOriginal;
|
||||
});
|
||||
}
|
||||
@@ -1639,12 +1638,12 @@ export function UniversalFormModalComponent({
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 🆕 연쇄 드롭다운 처리 (selectOptions.type === "cascading" 방식)
|
||||
if (field.selectOptions?.type === "cascading" && field.selectOptions?.cascading?.parentField) {
|
||||
const cascadingOpts = field.selectOptions.cascading;
|
||||
const parentValue = formData[cascadingOpts.parentField];
|
||||
|
||||
|
||||
// selectOptions 기반 cascading config를 CascadingDropdownConfig 형태로 변환
|
||||
const cascadingConfig: CascadingDropdownConfig = {
|
||||
enabled: true,
|
||||
@@ -2393,7 +2392,7 @@ function SelectField({ fieldId, value, onChange, optionConfig, placeholder, disa
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [inputValue, setInputValue] = useState(value || "");
|
||||
|
||||
|
||||
const allowCustomInput = optionConfig?.allowCustomInput || false;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -2433,14 +2432,14 @@ function SelectField({ fieldId, value, onChange, optionConfig, placeholder, disa
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="absolute right-0 top-0 h-full px-2 hover:bg-transparent"
|
||||
className="absolute top-0 right-0 h-full px-2 hover:bg-transparent"
|
||||
onClick={() => !disabled && !loading && setOpen(!open)}
|
||||
disabled={disabled || loading}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
|
||||
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<ChevronsUpDown className="h-4 w-4 text-muted-foreground" />
|
||||
<ChevronsUpDown className="text-muted-foreground h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -2463,12 +2462,7 @@ function SelectField({ fieldId, value, onChange, optionConfig, placeholder, disa
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
value === option.value ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<Check className={cn("mr-2 h-4 w-4", value === option.value ? "opacity-100" : "opacity-0")} />
|
||||
{option.label}
|
||||
</CommandItem>
|
||||
))}
|
||||
|
||||
@@ -47,7 +47,7 @@ export class V2InputRenderer extends AutoRegisteringComponentRenderer {
|
||||
const style = component.style || {};
|
||||
const labelDisplay = style.labelDisplay ?? (component as any).labelDisplay;
|
||||
// labelDisplay: true → 라벨 표시, false → 숨김, undefined → 기존 동작 유지(숨김)
|
||||
const effectiveLabel = labelDisplay === true ? (style.labelText || component.label) : undefined;
|
||||
const effectiveLabel = labelDisplay === true ? style.labelText || component.label : undefined;
|
||||
|
||||
return (
|
||||
<V2Input
|
||||
|
||||
@@ -533,14 +533,14 @@ export class ButtonActionExecutor {
|
||||
// 🆕 저장 전 이벤트 발생 (SelectedItemsDetailInput 등에서 최신 데이터 수집)
|
||||
// context.formData를 이벤트 detail에 포함하여 직접 수정 가능하게 함
|
||||
// skipDefaultSave 플래그를 통해 기본 저장 로직을 건너뛸 수 있음
|
||||
|
||||
|
||||
// 🔧 디버그: beforeFormSave 이벤트 전 formData 확인
|
||||
console.log("🔍 [handleSave] beforeFormSave 이벤트 전:", {
|
||||
keys: Object.keys(context.formData || {}),
|
||||
hasCompanyImage: "company_image" in (context.formData || {}),
|
||||
companyImageValue: context.formData?.company_image,
|
||||
});
|
||||
|
||||
|
||||
const beforeSaveEventDetail = {
|
||||
formData: context.formData,
|
||||
skipDefaultSave: false,
|
||||
@@ -555,7 +555,7 @@ export class ButtonActionExecutor {
|
||||
|
||||
// 약간의 대기 시간을 주어 이벤트 핸들러가 formData를 업데이트할 수 있도록 함
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
|
||||
// 🔧 디버그: beforeFormSave 이벤트 후 formData 확인
|
||||
console.log("🔍 [handleSave] beforeFormSave 이벤트 후:", {
|
||||
keys: Object.keys(context.formData || {}),
|
||||
@@ -1626,7 +1626,9 @@ export class ButtonActionExecutor {
|
||||
// saveResult.data.data.data = 실제 폼 데이터 { sabun, user_name... }
|
||||
const savedRecord = saveResult?.data?.data || saveResult?.data || {};
|
||||
const actualFormData = savedRecord?.data || savedRecord;
|
||||
const formData: Record<string, any> = (Object.keys(actualFormData).length > 0 ? actualFormData : context.formData || {}) as Record<string, any>;
|
||||
const formData: Record<string, any> = (
|
||||
Object.keys(actualFormData).length > 0 ? actualFormData : context.formData || {}
|
||||
) as Record<string, any>;
|
||||
console.log("📦 [executeAfterSaveControl] savedRecord 구조:", Object.keys(savedRecord));
|
||||
console.log("📦 [executeAfterSaveControl] actualFormData 추출:", Object.keys(formData));
|
||||
console.log("📦 [executeAfterSaveControl] formData.sabun:", formData.sabun);
|
||||
@@ -2924,8 +2926,7 @@ export class ButtonActionExecutor {
|
||||
|
||||
if (v2ListComponent) {
|
||||
dataSourceId =
|
||||
v2ListComponent.componentConfig.dataSource?.table ||
|
||||
v2ListComponent.componentConfig.tableName;
|
||||
v2ListComponent.componentConfig.dataSource?.table || v2ListComponent.componentConfig.tableName;
|
||||
console.log("✨ V2List 자동 감지:", {
|
||||
componentId: v2ListComponent.id,
|
||||
tableName: dataSourceId,
|
||||
@@ -3061,7 +3062,7 @@ export class ButtonActionExecutor {
|
||||
// 🔧 수정: openModalWithData는 "신규 등록 + 연결 데이터 전달"용이므로
|
||||
// editData가 아닌 splitPanelParentData로 전달해야 채번 등이 정상 작동함
|
||||
const isPassDataMode = passSelectedData && selectedData.length > 0;
|
||||
|
||||
|
||||
// 🔧 isEditMode 옵션이 명시적으로 true인 경우에만 수정 모드로 처리
|
||||
const useAsEditData = config.isEditMode === true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user