Compare commits
2 Commits
main
...
common/fea
| Author | SHA1 | Date |
|---|---|---|
|
|
1170e34d4e | |
|
|
aacedc004c |
|
|
@ -121,45 +121,45 @@ export const RepeaterInput: React.FC<RepeaterInputProps> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
// 🆕 초기 로드 시 계산식 필드 자동 업데이트 (한 번만 실행)
|
||||
// 🆕 초기 로드 시 계산식 필드 자동 업데이트 (한 번만 실행)
|
||||
const calculatedFields = fields.filter((f) => f.type === "calculated");
|
||||
|
||||
if (calculatedFields.length > 0 && !initialCalcDoneRef.current) {
|
||||
if (calculatedFields.length > 0 && !initialCalcDoneRef.current) {
|
||||
const updatedValue = value.map((item) => {
|
||||
const updatedItem = { ...item };
|
||||
let hasChange = false;
|
||||
const updatedItem = { ...item };
|
||||
let hasChange = false;
|
||||
|
||||
calculatedFields.forEach((calcField) => {
|
||||
const calculatedValue = calculateValue(calcField.formula, updatedItem);
|
||||
if (calculatedValue !== null && updatedItem[calcField.name] !== calculatedValue) {
|
||||
updatedItem[calcField.name] = calculatedValue;
|
||||
hasChange = true;
|
||||
const calculatedValue = calculateValue(calcField.formula, updatedItem);
|
||||
if (calculatedValue !== null && updatedItem[calcField.name] !== calculatedValue) {
|
||||
updatedItem[calcField.name] = calculatedValue;
|
||||
hasChange = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 🆕 기존 레코드임을 표시 (id가 있는 경우)
|
||||
if (updatedItem.id) {
|
||||
updatedItem._existingRecord = true;
|
||||
}
|
||||
|
||||
return hasChange ? updatedItem : item;
|
||||
});
|
||||
|
||||
// 🆕 기존 레코드임을 표시 (id가 있는 경우)
|
||||
if (updatedItem.id) {
|
||||
updatedItem._existingRecord = true;
|
||||
}
|
||||
setItems(updatedValue);
|
||||
initialCalcDoneRef.current = true;
|
||||
|
||||
return hasChange ? updatedItem : item;
|
||||
});
|
||||
|
||||
setItems(updatedValue);
|
||||
initialCalcDoneRef.current = true;
|
||||
|
||||
// 계산된 값이 있으면 onChange 호출 (초기 1회만)
|
||||
const dataWithMeta = config.targetTable
|
||||
? updatedValue.map((item) => ({ ...item, _targetTable: config.targetTable }))
|
||||
: updatedValue;
|
||||
onChange?.(dataWithMeta);
|
||||
} else {
|
||||
// 🆕 기존 레코드 플래그 추가
|
||||
// 계산된 값이 있으면 onChange 호출 (초기 1회만)
|
||||
const dataWithMeta = config.targetTable
|
||||
? updatedValue.map((item) => ({ ...item, _targetTable: config.targetTable }))
|
||||
: updatedValue;
|
||||
onChange?.(dataWithMeta);
|
||||
} else {
|
||||
// 🆕 기존 레코드 플래그 추가
|
||||
const valueWithFlag = value.map((item) => ({
|
||||
...item,
|
||||
_existingRecord: !!item.id,
|
||||
}));
|
||||
setItems(valueWithFlag);
|
||||
...item,
|
||||
_existingRecord: !!item.id,
|
||||
}));
|
||||
setItems(valueWithFlag);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
|
|
@ -200,14 +200,25 @@ export const RepeaterInput: React.FC<RepeaterInputProps> = ({
|
|||
const currentDeletedIds = deletedItemIdsRef.current;
|
||||
console.log("🗑️ [RepeaterInput] 현재 삭제 목록:", currentDeletedIds);
|
||||
|
||||
const dataWithMeta = config.targetTable
|
||||
? newItems.map((item, idx) => ({
|
||||
// 🆕 빈 배열일 때도 삭제 ID를 전달해야 함
|
||||
let dataWithMeta: any[];
|
||||
if (config.targetTable) {
|
||||
if (newItems.length > 0) {
|
||||
// 항목이 있으면 첫 번째 항목에 삭제 ID 포함
|
||||
dataWithMeta = newItems.map((item, idx) => ({
|
||||
...item,
|
||||
_targetTable: config.targetTable,
|
||||
// 첫 번째 항목에만 삭제 ID 목록 포함
|
||||
...(idx === 0 ? { _deletedItemIds: currentDeletedIds } : {}),
|
||||
}))
|
||||
: newItems;
|
||||
}));
|
||||
} else if (currentDeletedIds.length > 0) {
|
||||
// 🆕 모든 항목 삭제 시 삭제 ID만 포함된 메타 객체 전달
|
||||
dataWithMeta = [{ _targetTable: config.targetTable, _deletedItemIds: currentDeletedIds, _deleteOnly: true }];
|
||||
} else {
|
||||
dataWithMeta = [];
|
||||
}
|
||||
} else {
|
||||
dataWithMeta = newItems;
|
||||
}
|
||||
|
||||
console.log("🗑️ [RepeaterInput] onChange 호출 - dataWithMeta:", dataWithMeta);
|
||||
onChange?.(dataWithMeta);
|
||||
|
|
@ -372,11 +383,11 @@ export const RepeaterInput: React.FC<RepeaterInputProps> = ({
|
|||
// 천 단위 구분자
|
||||
let result =
|
||||
format?.useThousandSeparator !== false
|
||||
? formattedValue.toLocaleString("ko-KR", {
|
||||
minimumFractionDigits: format?.minimumFractionDigits ?? 0,
|
||||
maximumFractionDigits: format?.maximumFractionDigits ?? format?.decimalPlaces ?? 0,
|
||||
})
|
||||
: formattedValue.toString();
|
||||
? formattedValue.toLocaleString("ko-KR", {
|
||||
minimumFractionDigits: format?.minimumFractionDigits ?? 0,
|
||||
maximumFractionDigits: format?.maximumFractionDigits ?? format?.decimalPlaces ?? 0,
|
||||
})
|
||||
: formattedValue.toString();
|
||||
|
||||
// 접두사/접미사 추가
|
||||
if (format?.prefix) result = format.prefix + result;
|
||||
|
|
|
|||
|
|
@ -802,6 +802,7 @@ export class ButtonActionExecutor {
|
|||
}
|
||||
}
|
||||
|
||||
// 🆕 배열이 비어있지 않거나, 하나라도 항목이 있으면 처리
|
||||
if (Array.isArray(parsedValue) && parsedValue.length > 0) {
|
||||
const firstItem = parsedValue[0];
|
||||
const deletedItemIds = firstItem?._deletedItemIds;
|
||||
|
|
@ -811,6 +812,7 @@ export class ButtonActionExecutor {
|
|||
firstItemKeys: firstItem ? Object.keys(firstItem) : [],
|
||||
deletedItemIds,
|
||||
targetTable,
|
||||
isDeleteOnly: firstItem?._deleteOnly,
|
||||
});
|
||||
|
||||
if (deletedItemIds && deletedItemIds.length > 0 && targetTable) {
|
||||
|
|
@ -868,6 +870,12 @@ export class ButtonActionExecutor {
|
|||
// _targetTable이 없거나, _repeatScreenModal_ 키면 스킵 (다른 로직에서 처리)
|
||||
if (!repeaterTargetTable || fieldKey.startsWith("_repeatScreenModal_")) continue;
|
||||
|
||||
// 🆕 _deleteOnly 플래그가 있으면 INSERT/UPDATE 건너뛰기 (삭제 전용)
|
||||
if (firstItem?._deleteOnly) {
|
||||
console.log(`⏭️ [handleSave] 삭제 전용 데이터 - INSERT/UPDATE 스킵: ${fieldKey}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`📦 [handleSave] RepeaterFieldGroup 데이터 저장: ${fieldKey} → ${repeaterTargetTable}`, {
|
||||
itemCount: parsedData.length,
|
||||
});
|
||||
|
|
@ -2839,10 +2847,7 @@ export class ButtonActionExecutor {
|
|||
* EditModal 등 외부에서도 호출 가능하도록 public으로 변경
|
||||
* 다중 제어 순차 실행 지원
|
||||
*/
|
||||
public static async executeAfterSaveControl(
|
||||
config: ButtonActionConfig,
|
||||
context: ButtonActionContext,
|
||||
): Promise<void> {
|
||||
public static async executeAfterSaveControl(config: ButtonActionConfig, context: ButtonActionContext): Promise<void> {
|
||||
console.log("🎯 저장 후 제어 실행:", {
|
||||
enableDataflowControl: config.enableDataflowControl,
|
||||
dataflowConfig: config.dataflowConfig,
|
||||
|
|
@ -4306,19 +4311,20 @@ export class ButtonActionExecutor {
|
|||
if (userId) {
|
||||
try {
|
||||
const { apiClient } = await import("@/lib/api/client");
|
||||
const statusTableName = config.trackingStatusTableName || this.trackingConfig?.trackingStatusTableName || context.tableName || "vehicles";
|
||||
const statusTableName =
|
||||
config.trackingStatusTableName ||
|
||||
this.trackingConfig?.trackingStatusTableName ||
|
||||
context.tableName ||
|
||||
"vehicles";
|
||||
const keyField = config.trackingStatusKeyField || this.trackingConfig?.trackingStatusKeyField || "user_id";
|
||||
|
||||
// DB에서 현재 차량 정보 조회
|
||||
const vehicleResponse = await apiClient.post(
|
||||
`/table-management/tables/${statusTableName}/data`,
|
||||
{
|
||||
page: 1,
|
||||
size: 1,
|
||||
search: { [keyField]: userId },
|
||||
autoFilter: true,
|
||||
},
|
||||
);
|
||||
const vehicleResponse = await apiClient.post(`/table-management/tables/${statusTableName}/data`, {
|
||||
page: 1,
|
||||
size: 1,
|
||||
search: { [keyField]: userId },
|
||||
autoFilter: true,
|
||||
});
|
||||
|
||||
const vehicleData = vehicleResponse.data?.data?.data?.[0] || vehicleResponse.data?.data?.rows?.[0];
|
||||
if (vehicleData) {
|
||||
|
|
@ -4335,14 +4341,18 @@ export class ButtonActionExecutor {
|
|||
// 마지막 위치 저장 (추적 중이었던 경우에만)
|
||||
if (isTrackingActive) {
|
||||
// DB 값 우선, 없으면 formData 사용
|
||||
const departure = dbDeparture ||
|
||||
this.trackingContext?.formData?.[this.trackingConfig?.trackingDepartureField || "departure"] || null;
|
||||
const arrival = dbArrival ||
|
||||
this.trackingContext?.formData?.[this.trackingConfig?.trackingArrivalField || "arrival"] || null;
|
||||
const departure =
|
||||
dbDeparture ||
|
||||
this.trackingContext?.formData?.[this.trackingConfig?.trackingDepartureField || "departure"] ||
|
||||
null;
|
||||
const arrival =
|
||||
dbArrival || this.trackingContext?.formData?.[this.trackingConfig?.trackingArrivalField || "arrival"] || null;
|
||||
const departureName = this.trackingContext?.formData?.["departure_name"] || null;
|
||||
const destinationName = this.trackingContext?.formData?.["destination_name"] || null;
|
||||
const vehicleId = dbVehicleId ||
|
||||
this.trackingContext?.formData?.[this.trackingConfig?.trackingVehicleIdField || "vehicle_id"] || null;
|
||||
const vehicleId =
|
||||
dbVehicleId ||
|
||||
this.trackingContext?.formData?.[this.trackingConfig?.trackingVehicleIdField || "vehicle_id"] ||
|
||||
null;
|
||||
|
||||
await this.saveLocationToHistory(
|
||||
tripId,
|
||||
|
|
|
|||
Loading…
Reference in New Issue