feat(wbs-ux): inject 3 additional live operational features into SystemSettingsView (SignalR Socket Chip, DbUp Tag, Live Log Terminal)
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 12s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 21s
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 13s
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 10s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 9s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped

This commit is contained in:
2026-07-25 20:27:44 +09:00
parent a46744bd18
commit e8550dc583
+88 -24
View File
@@ -27,7 +27,7 @@ interface SettingItem {
status: 'ACTIVE' | 'WARNING' | 'BLOCKED';
updated_at: string;
note: string;
lock_version: number; // 낙관적 락 버전
lock_version: number;
attachments?: string[];
audit_history?: AuditLog[];
}
@@ -41,11 +41,17 @@ const items = ref<SettingItem[]>([]);
const selectedItem = ref<SettingItem | null>(null);
const isEditMode = ref(false);
// 3. 현장감 텔레메트리 & 배치 프로그레스 상태
// 3. 현장감 텔레메트리, 소켓, 로그 스트리밍 상태
const pingMs = ref(4);
const isBatchRunning = ref(false);
const batchProgress = ref(0);
const dbConnections = ref(4);
const liveLogs = ref<string[]>([
'[14:00:01] [INFO] SignalR WebSocket Hub connected to wss://localhost:5173/hub',
'[14:00:05] [INFO] DbUp Migration schema verified (v2026.07.25_001)',
'[14:02:10] [INFO] Factor RSI calculated successfully (0.012s)',
'[14:02:15] [WARN] D2 Cash threshold check passed (500,000,000 KRW target)'
]);
// 4. 페이지네이션 및 엑셀 드롭다운 상태
const currentPage = ref(1);
@@ -135,12 +141,14 @@ const runFactorBatch = () => {
if (isBatchRunning.value) return;
isBatchRunning.value = true;
batchProgress.value = 0;
liveLogs.value.unshift(`[${new Date().toLocaleTimeString()}] [EXEC] Triggered factor recalculation batch pipeline`);
showToast('⚡ 팩터 재계산 비동기 배치 작업이 트리거되었습니다.', 'success');
const timer = setInterval(() => {
batchProgress.value += 25;
if (batchProgress.value >= 100) {
clearInterval(timer);
isBatchRunning.value = false;
liveLogs.value.unshift(`[${new Date().toLocaleTimeString()}] [SUCCESS] Batch pipeline finished in 1.2s`);
showToast('✅ 팩터 재계산 배치가 성공적으로 완료되었습니다 (소요시간: 1.2s)', 'success');
}
}, 300);
@@ -175,6 +183,7 @@ const saveModalItem = () => {
items.value.unshift({ ...modalItem.value });
selectedItem.value = { ...modalItem.value };
isModalOpen.value = false;
liveLogs.value.unshift(`[${new Date().toLocaleTimeString()}] [CREATE] Added new setting key: ${modalItem.value.setting_key}`);
showToast('신규 설정 항목이 성공적으로 등록되었습니다.', 'success');
};
@@ -182,7 +191,7 @@ const saveDetailForm = () => {
if (!selectedItem.value) return;
const idx = items.value.findIndex(i => i.id === selectedItem.value?.id);
if (idx !== -1) {
selectedItem.value.lock_version += 1; // 락 버전 증가
selectedItem.value.lock_version += 1;
if (!selectedItem.value.audit_history) selectedItem.value.audit_history = [];
selectedItem.value.audit_history.unshift({
timestamp: new Date().toISOString().slice(0, 16).replace('T', ' '),
@@ -191,6 +200,7 @@ const saveDetailForm = () => {
});
items.value[idx] = JSON.parse(JSON.stringify(selectedItem.value));
isEditMode.value = false;
liveLogs.value.unshift(`[${new Date().toLocaleTimeString()}] [UPDATE] Key '${selectedItem.value.setting_key}' saved (Ver v${selectedItem.value.lock_version})`);
showToast(`PostgreSQL 원장 설정 변경사항이 저장되었습니다 (Lock Ver: v${selectedItem.value.lock_version}).`, 'success');
}
};
@@ -198,6 +208,7 @@ const saveDetailForm = () => {
const deleteSelectedItem = () => {
if (!selectedItem.value) return;
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;
showToast('항목이 성공적으로 삭제되었습니다.', 'warning');
@@ -206,6 +217,7 @@ const deleteSelectedItem = () => {
const triggerExport = (fmt: string) => {
showExportMenu.value = false;
liveLogs.value.unshift(`[${new Date().toLocaleTimeString()}] [EXPORT] Exporting data stream as ${fmt}`);
showToast(`${fmt} 형식으로 대용량 스트리밍 내보내기를 시작합니다.`, 'success');
};
@@ -229,16 +241,18 @@ onMounted(loadData);
<div class="toolbar-title">
<div class="title-with-chip">
<h3 class="title-text"> SCR-07: 현장감 퀀트 CRUD 매니저</h3>
<span class="db-ping-chip">🟢 PostgreSQL Live ({{ pingMs }}ms)</span>
<span class="db-ping-chip">🟢 Live ({{ pingMs }}ms)</span>
<span class="socket-chip">📡 SignalR Connected</span>
</div>
<span class="subtitle-text">Master-Detail, AG Grid, Batch Progress, Lock Guard, Telemetry, Toast, 더존 컴포넌트</span>
<span class="subtitle-text">Master-Detail, AG Grid, Batch Progress, Lock Guard, Telemetry, Log Terminal, 더존 컴포넌트</span>
</div>
<div class="toolbar-actions">
<span class="dbup-version-tag">🛡 DbUp: v2026.07.25</span>
<button class="btn-batch-trigger" @click="runFactorBatch" :disabled="isBatchRunning">
{{ isBatchRunning ? `⏳ 계산중 ${batchProgress}%` : '⚡ 팩터 배치 실행' }}
</button>
<button class="btn-guide-spec" @click="isGuideModalOpen = true">💡 16 명세 팝업</button>
<button class="btn-guide-spec" @click="isGuideModalOpen = true">💡 18 명세 팝업</button>
<div class="search-box">
<input type="text" class="search-input" placeholder="설정 키/설명 검색..." v-model="searchKeyword" />
</div>
@@ -374,6 +388,16 @@ onMounted(loadData);
<QuantTextArea v-model="selectedItem.note" :disabled="!isEditMode" :rows="2" />
</div>
<!-- 실시간 파이프라인 로그 터미널 -->
<div class="live-log-terminal">
<div class="terminal-header">💻 실시간 퀀트 엔진 로그 터미널</div>
<div class="terminal-body">
<div v-for="(log, idx) in liveLogs.slice(0, 3)" :key="idx" class="log-line">
{{ log }}
</div>
</div>
</div>
<!-- 변경 감사 이력 타임라인 (Audit History Timeline) -->
<div class="audit-timeline-section" v-if="selectedItem.audit_history && selectedItem.audit_history.length > 0">
<h5 class="timeline-title">🕒 변경 감사 이력 타임라인 (Audit Trail)</h5>
@@ -430,31 +454,33 @@ onMounted(loadData);
</div>
</div>
<!-- 16 CRUD & 현장감 컴포넌트 명세 가이드 Modal -->
<!-- 18 CRUD & 현장감 컴포넌트 명세 가이드 Modal -->
<div class="modal-backdrop" v-if="isGuideModalOpen">
<div class="modal-dialog guide-modal">
<div class="modal-header">
<h4 class="modal-title">💡 화면의 현장감 넘치는 16 CRUD 컴포넌트 스펙</h4>
<h4 class="modal-title">💡 화면의 현장감 넘치는 18 CRUD 컴포넌트 스펙</h4>
<button class="btn-close-modal" @click="isGuideModalOpen = false"></button>
</div>
<div class="modal-body guide-body">
<ul class="guide-list">
<li><strong>1. Live PostgreSQL Ping 레이턴시 </strong>: 4ms 실시간 상태</li>
<li><strong>2. 팩터 재계산 비동기 배치 버튼 & 진행률 </strong>: 0~100% 모의 트랜잭션</li>
<li><strong>3. 낙관적 버전 인디케이터 (Lock Guard)</strong>: 동시성 충돌 방지 v1~v5</li>
<li><strong>4. 실시간 텔레메트리 시스템 </strong>: CPU/RAM/DB Pool 현장감 지표</li>
<li><strong>5. 검색 & 핫키 </strong>: F3/F4/F5/F7 더존 표준 핫키</li>
<li><strong>6. 카테고리 </strong>: ALL/BUDGET/FACTOR/RISK 1-Click 필터</li>
<li><strong>7. 6:4 Master-Detail Split Pane</strong>: 8px 물리 구분선 뷰포트</li>
<li><strong>8. AG Grid 데이터 테이블</strong>: 정렬, 필터, 단일 선택, Status Chips</li>
<li><strong>9. 페이지네이션 </strong>: Page 1 of 1 건수 페이지 컨트롤러</li>
<li><strong>10. 더존 6 표준 입력 마스크</strong>: Input, ComboBox, Radio, TextArea </li>
<li><strong>11. 읽기/편집 모드 토글</strong>: 👁 읽기 / 편집 전환 스위치</li>
<li><strong>12. 증빙 파일 첨부 드롭존</strong>: Drag & drop 파일 첨부 </li>
<li><strong>13. 감사 타임라인</strong>: 수정자/일시 Audit Trail 스캔</li>
<li><strong>14. 신규 등록 Modal</strong>: 팝업 대화상자 세트</li>
<li><strong>15. 플로팅 토스트 알림</strong>: 3 자동소멸 피드백</li>
<li><strong>16. 내보내기 드롭다운 팝업</strong>: Excel (.xlsx) / CSV 내보내기</li>
<li><strong>2. SignalR WebSocket 소켓 연결 </strong>: 소켓 연결 라이브 지표</li>
<li><strong>3. DbUp 스키마 마이그레이션 뱃지</strong>: DB 마이그레이션 태그</li>
<li><strong>4. 실시간 파이프라인 로그 터미널 </strong>: 엔진 트랜잭션 스트리밍</li>
<li><strong>5. 팩터 재계산 비동기 배치 버튼 & 진행률 </strong>: 0~100% 모의 트랜잭션</li>
<li><strong>6. 낙관적 버전 인디케이터 (Lock Guard)</strong>: 동시성 충돌 방지 v1~v5</li>
<li><strong>7. 실시간 텔레메트리 시스템 </strong>: CPU/RAM/DB Pool 현장감 지표</li>
<li><strong>8. 검색 & 핫키 툴바</strong>: F3/F4/F5/F7 더존 표준 핫키</li>
<li><strong>9. 카테고리 </strong>: ALL/BUDGET/FACTOR/RISK 1-Click 필터</li>
<li><strong>10. 6:4 Master-Detail Split Pane</strong>: 8px 물리 구분선 뷰포트</li>
<li><strong>11. AG Grid 데이터 테이블</strong>: 정렬, 필터, 단일 선택, Status Chips</li>
<li><strong>12. 페이지네이션 </strong>: Page 1 of 1 건수 페이지 컨트롤러</li>
<li><strong>13. 더존 6 표준 마스크</strong>: Input, ComboBox, Radio, TextArea </li>
<li><strong>14. 읽기/편집 모드 토글</strong>: 👁 읽기 / 편집 전환 스위치</li>
<li><strong>15. 증빙 파일 첨부 드롭존</strong>: Drag & drop 파일 첨부 </li>
<li><strong>16. 감사 이력 타임라인</strong>: 수정자/일시 Audit Trail 스캔</li>
<li><strong>17. 신규 등록 Modal</strong>: 팝업 대화상자 세트</li>
<li><strong>18. 플로팅 토스트 알림 & 엑셀 팝업</strong>: 성공/경고 피드백 내보내기</li>
</ul>
</div>
<div class="modal-footer">
@@ -477,14 +503,32 @@ onMounted(loadData);
position: relative;
}
.title-with-chip { display: flex; align-items: center; gap: 10px; }
.title-with-chip { display: flex; align-items: center; gap: 8px; }
.db-ping-chip {
background: #166534;
color: #DCFCE7;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.65rem;
font-weight: 700;
}
.socket-chip {
background: #1E40AF;
color: #DBEAFE;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.65rem;
font-weight: 700;
}
.dbup-version-tag {
font-size: 0.7rem;
font-weight: 700;
color: #CBD5E1;
background: rgba(255,255,255,0.1);
padding: 2px 6px;
border-radius: 4px;
}
.btn-batch-trigger {
@@ -798,6 +842,26 @@ onMounted(loadData);
margin-top: 4px;
}
.live-log-terminal {
background: #0F172A;
color: #38BDF8;
border-radius: 4px;
padding: 6px 10px;
font-family: monospace;
font-size: 0.65rem;
}
.terminal-header {
color: #94A3B8;
border-bottom: 1px solid #334155;
padding-bottom: 2px;
margin-bottom: 4px;
font-weight: 700;
}
.terminal-body { display: flex; flex-direction: column; gap: 2px; }
.log-line { word-break: break-all; }
.audit-timeline-section {
border-top: 1px solid #E2E8F0;
padding-top: 8px;