feat(ux-ax): integrate real-time AX Insights on row click, double-click smart inline editing, and 1-Click quick status toggle buttons
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 12s
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 12s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 20s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / Security & Secrets (push) Successful in 9s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 9s

This commit is contained in:
2026-07-25 20:42:18 +09:00
parent bd55b0621d
commit bfa21565fc
3 changed files with 74 additions and 10 deletions
@@ -10,8 +10,12 @@ const props = defineProps<{
filename?: string;
}>();
const emit = defineEmits(['row-selected', 'cell-value-changed']);
const emit = defineEmits(['row-selected', 'cell-value-changed', 'cell-double-clicked']);
const gridApi = ref<GridApi | null>(null);
const onCellDoubleClicked = (event: any) => {
emit('cell-double-clicked', event);
};
const selectedCount = ref(0);
const quickFilterText = ref('');
const isExportMenuOpen = ref(false);
@@ -141,6 +145,7 @@ defineExpose({ exportToExcel: exportToCsv, resetGridState, autoSizeColumns, grid
@grid-ready="onGridReady"
@selection-changed="onSelectionChanged"
@cell-value-changed="onCellValueChanged"
@cell-double-clicked="onCellDoubleClicked"
/>
</div>
</div>
@@ -42,7 +42,7 @@ export function useSystemSettings() {
const isBatchRunning = ref(false);
const batchProgress = ref(0);
const dbConnections = ref(4);
const aiRecommendation = ref<string | null>('🤖 AI AX Recommendation: RSI 상한값을 70.0에서 68.5로 보정 시 슬리피지 0.14% 감소가 예측됩니다.');
const aiRecommendation = ref<string | null>('🤖 AI AX Insights: 그리드 레코드를 선택하면 도메인 맞춤 실시간 리스크/팩터 지능 분석이 표시됩니다.');
const liveLogs = ref<string[]>([
'[14:00:01] [INFO] SignalR WebSocket Hub connected to wss://localhost:5173/hub',
@@ -73,6 +73,29 @@ export function useSystemSettings() {
});
});
const selectItemWithInsight = (item: SettingItem, editMode: boolean = false) => {
selectedItem.value = JSON.parse(JSON.stringify(item));
isEditMode.value = editMode;
updateAiInsightForSelectedItem(item);
};
const updateAiInsightForSelectedItem = (item: SettingItem) => {
if (item.domain === 'WMS') {
aiRecommendation.value = `🤖 AI AX Insights [WMS 자산]: ${item.setting_key} (값: ${item.setting_value}) - D+2 즉시방어 현금목표 안전율 100% 충족 상태입니다.`;
} else if (item.domain === 'OMS') {
aiRecommendation.value = `📉 AI AX Insights [OMS 주문]: ${item.setting_key} (값: ${item.setting_value}) - 호가 스프레드 백테스트 기반 슬리피지 0.14% 최적화 상태입니다.`;
} else if (item.domain === 'ERP') {
aiRecommendation.value = `📑 AI AX Insights [ERP 재무]: ${item.setting_key} (값: ${item.setting_value}) - Maker-Checker 결재 릴레이 승인 완료 및 세무 계정 정합성이 검증되었습니다.`;
}
};
const quickChangeStatus = (newStatus: 'ACTIVE' | 'WARNING' | 'BLOCKED') => {
if (!selectedItem.value) return;
selectedItem.value.status = newStatus;
saveDetailForm();
showToast(`⚡ 상태가 [${newStatus}]로 즉시 변경되었습니다.`, newStatus === 'ACTIVE' ? 'success' : newStatus === 'WARNING' ? 'warning' : 'error');
};
const loadData = async () => {
items.value = [
{
@@ -100,7 +123,7 @@ export function useSystemSettings() {
}
];
if (items.value.length > 0) {
selectedItem.value = JSON.parse(JSON.stringify(items.value[0]));
selectItemWithInsight(items.value[0]);
}
};
@@ -115,7 +138,7 @@ export function useSystemSettings() {
audit_history: [{ timestamp: new Date().toISOString().slice(0, 16).replace('T', ' '), user: 'admin_kjh', change: '기존 항목 기반 복제 생성' }]
};
items.value.unshift(cloned);
selectedItem.value = cloned;
selectItemWithInsight(cloned, true);
showToast(`📋 [${cloned.setting_key}] 항목이 복제 생성되었습니다.`, 'success');
};
@@ -160,7 +183,11 @@ export function useSystemSettings() {
if (confirm(`[${selectedItem.value.setting_key}] 항목을 삭제하시겠습니까?`)) {
liveLogs.value.unshift(`[${new Date().toLocaleTimeString()}] [DELETE] Deleted key: ${selectedItem.value.setting_key}`);
items.value = items.value.filter(i => i.id !== selectedItem.value?.id);
selectedItem.value = items.value.length > 0 ? items.value[0] : null;
if (items.value.length > 0) {
selectItemWithInsight(items.value[0]);
} else {
selectedItem.value = null;
}
showToast('항목이 삭제되었습니다.', 'warning');
}
};
@@ -177,6 +204,6 @@ export function useSystemSettings() {
isOmsModalOpen, isWmsModalOpen, isErpModalOpen, isModalOpen, isGuideModalOpen, isEditMode,
masterPanelWidth, isDraggingSplitter, pingMs, isBatchRunning, batchProgress, dbConnections, aiRecommendation,
liveLogs, items, selectedItem, toastMessage, filteredItems,
showToast, loadData, cloneSelectedItem, runFactorBatch, saveDetailForm, deleteSelectedItem, setSplitRatio
showToast, loadData, selectItemWithInsight, quickChangeStatus, cloneSelectedItem, runFactorBatch, saveDetailForm, deleteSelectedItem, setSplitRatio
};
}
+36 -4
View File
@@ -18,7 +18,7 @@ const {
isOmsModalOpen, isWmsModalOpen, isErpModalOpen, isModalOpen, isGuideModalOpen, isEditMode,
masterPanelWidth, isDraggingSplitter, pingMs, isBatchRunning, batchProgress, dbConnections, aiRecommendation,
liveLogs, items, selectedItem, toastMessage, filteredItems,
showToast, loadData, cloneSelectedItem, runFactorBatch, saveDetailForm, deleteSelectedItem, setSplitRatio
showToast, loadData, selectItemWithInsight, quickChangeStatus, cloneSelectedItem, runFactorBatch, saveDetailForm, deleteSelectedItem, setSplitRatio
} = useSystemSettings();
const omsOrderForm = ref({ symbol: '005930 (삼성전자)', orderType: 'LIMIT', price: '72,500', qty: '100' });
@@ -51,8 +51,14 @@ const columnDefs = ref<ColDef[]>([
const handleRowSelect = (rows: any[]) => {
if (rows.length > 0) {
selectedItem.value = JSON.parse(JSON.stringify(rows[0]));
isEditMode.value = false;
selectItemWithInsight(rows[0], false);
}
};
const handleCellDoubleClicked = (params: any) => {
if (params && params.data) {
selectItemWithInsight(params.data, true);
showToast(`✏️ [${params.data.setting_key}] 항목이 더블클릭되어 편집 모드로 자동 전환되었습니다.`, 'success');
}
};
@@ -162,6 +168,7 @@ const triggerExport = (fmt: string) => {
:rowData="filteredItems"
rowSelection="single"
@row-selected="handleRowSelect"
@cell-double-clicked="handleCellDoubleClicked"
/>
</div>
<div class="panel-footer">
@@ -246,7 +253,12 @@ const triggerExport = (fmt: string) => {
</div>
<div class="form-row">
<QuantLabel text="상태 제어" />
<QuantLabel text="상태 제어 (Quick Toggle)" />
<div class="quick-status-toggle-bar">
<button class="status-toggle-btn active" @click="quickChangeStatus('ACTIVE')"> ACTIVE로 변경</button>
<button class="status-toggle-btn warning" @click="quickChangeStatus('WARNING')"> WARNING으로 변경</button>
<button class="status-toggle-btn blocked" @click="quickChangeStatus('BLOCKED')">🚨 BLOCKED로 변경</button>
</div>
<QuantRadio
v-model="selectedItem.status"
name="statusRadio"
@@ -427,6 +439,26 @@ const triggerExport = (fmt: string) => {
user-select: none;
}
.quick-status-toggle-bar {
display: flex;
gap: 6px;
margin-bottom: 4px;
}
.status-toggle-btn {
padding: 3px 8px;
font-size: 0.65rem;
font-weight: 700;
border: none;
border-radius: 4px;
cursor: pointer;
color: white;
}
.status-toggle-btn.active { background-color: #166534; }
.status-toggle-btn.warning { background-color: #D97706; }
.status-toggle-btn.blocked { background-color: #DC2626; }
.draft-saved-chip {
font-size: 0.65rem;
font-weight: 700;