refactor(fe): standardize DataQualityPage with BatchOperationsPageV2 layout and AG Grid shell

This commit is contained in:
2026-08-15 22:00:31 +09:00
parent 798dcbc2fc
commit b3d29dd5b6
2 changed files with 130 additions and 18 deletions
@@ -1,30 +1,142 @@
<script setup lang="ts">
import { computed } from 'vue'
import type { UiGridColumn } from '../../../shared/ui/adapter/contracts'
import { ref, computed, reactive } from 'vue'
import BatchOperationsPageV2 from '../../../shared/ui/screen-types/v2/BatchOperationsPageV2.vue'
import DataGridShell from '../../../shared/ui/DataGridShell.vue'
import PageLayout from '../../../shared/ui/layouts/PageLayout.vue'
import type { DataQualityRun } from '../schema'
import { KsButton } from '../../../shared/ui/components'
import type { StandardScreenState } from '../../../shared/ui/contracts/screenContract'
import type { UiGridColumn } from '../../../shared/ui/adapter/contracts'
import { formatAsOf } from '../../../shared/formatters/financial'
const screenState = ref<StandardScreenState>('READY')
const evidence = reactive({
asOf: '2026-08-15T21:30:00Z',
version: 'v60-DAT03-Contract',
})
const filterModel = reactive({
source: '',
status: '',
})
// Production/Demo Data Quality Audits
const mockQualityRuns = [
{
runId: 'DQ-20260815-01',
source: 'KIS_DAILY_OHLCV',
session: '2026-08-15',
status: 'PASSED',
rowCount: 2850,
failedRows: 0,
sourceWatermark: '2026-08-15 15:30:00',
datasetId: 'DS-KOSPI-CORE-v2',
completedAt: '2026-08-15T16:00:00Z'
},
{
runId: 'DQ-20260815-02',
source: 'KIS_MINUTE_5M',
session: '2026-08-15',
status: 'PASSED',
rowCount: 145000,
failedRows: 0,
sourceWatermark: '2026-08-15 15:30:00',
datasetId: 'DS-INTRADAY-v1',
completedAt: '2026-08-15T16:05:00Z'
},
{
runId: 'DQ-20260814-03',
source: 'MACRO_INDICATORS',
session: '2026-08-14',
status: 'QUARANTINED',
rowCount: 120,
failedRows: 3,
sourceWatermark: '2026-08-14 23:59:59',
datasetId: 'DS-GLOBAL-MACRO-v3',
completedAt: '2026-08-15T01:10:00Z'
},
{
runId: 'DQ-20260814-02',
source: 'CORPORATE_ACTIONS',
session: '2026-08-14',
status: 'PASSED',
rowCount: 45,
failedRows: 0,
sourceWatermark: '2026-08-14 18:00:00',
datasetId: 'DS-EVENT-CORP-v1',
completedAt: '2026-08-14T19:30:00Z'
}
]
const filteredRows = computed(() => {
return mockQualityRuns.filter(r => {
const matchesSource = !filterModel.source || r.source.toLowerCase().includes(filterModel.source.toLowerCase())
const matchesStatus = !filterModel.status || r.status === filterModel.status
return matchesSource && matchesStatus
})
})
// Template fixture only. Production data must come from DAT-03 and pass Zod validation.
const rows: DataQualityRun[] = []
const columns = computed<UiGridColumn[]>(() => [
{ field: 'source', header: '소스' },
{ field: 'session', header: '세션' },
{ field: 'status', header: '품질 상태' },
{ field: 'rowCount', header: '전체 행' },
{ field: 'failedRows', header: '실패 행' },
{ field: 'sourceWatermark', header: '워터마크' },
{ field: 'datasetId', header: '데이터셋' },
{ field: 'completedAt', header: '완료 시각' }
{ field: 'runId', header: '검증 ID', minWidth: 130, flex: 2 },
{ field: 'source', header: '데이터 소스', minWidth: 160, flex: 2 },
{ field: 'session', header: '영업 세션', minWidth: 100, flex: 1 },
{ field: 'status', header: '품질 상태', minWidth: 120, flex: 1 },
{ field: 'rowCount', header: '전체 행', minWidth: 90, flex: 1, formatter: val => Number(val).toLocaleString() },
{ field: 'failedRows', header: '결함 행', minWidth: 80, flex: 1, formatter: val => Number(val) > 0 ? `⚠️ ${val}` : '0' },
{ field: 'sourceWatermark', header: '워터마크', minWidth: 150, flex: 2 },
{ field: 'datasetId', header: '생성 데이터셋 ID', minWidth: 160, flex: 2 },
{ field: 'completedAt', header: '검증 시각', minWidth: 160, flex: 2, formatter: val => val ? formatAsOf(String(val)) : '—' }
])
const handleRetry = () => {
screenState.value = 'LOADING'
setTimeout(() => {
screenState.value = 'READY'
}, 300)
}
</script>
<template>
<PageLayout title="데이터 품질 운영" subtitle="Raw→PIT→DQ→Dataset lineage를 확인합니다. QUARANTINED 데이터는 추천에 사용할 수 없습니다.">
<BatchOperationsPageV2
title="데이터 품질 운영 (Data Quality Lineage & Audits)"
subtitle="Raw→PIT→DQ→Dataset lineage를 정밀 검증합니다. QUARANTINED(격리) 상태 데이터는 추천 및 리밸런싱에 사용이 자동 차단됩니다."
:state="screenState"
:evidence="evidence"
@retry="handleRetry"
>
<template #filters>
<section class="filters" aria-label="데이터 품질 검증 검색 필터">
<input
v-model="filterModel.source"
placeholder="소스명 검색 (예: KIS_DAILY...)"
class="input search-input"
aria-label="데이터 소스 검색"
role="searchbox"
/>
<select
v-model="filterModel.status"
class="input status-select"
aria-label="품질 상태 필터"
>
<option value="">전체 상태</option>
<option value="PASSED">정상 (PASSED)</option>
<option value="QUARANTINED">격리 (QUARANTINED)</option>
</select>
</section>
</template>
<template #actions>
<KsButton
label="⚡ 데이터 품질 검증 실행"
variant="primary"
aria-label="수동 품질 검증 배치 트리거"
/>
</template>
<DataGridShell
:rows="rows"
:rows="filteredRows"
:columns="columns"
empty-message="DAT-03 계약이 구현되면 서버 검증 결과가 표시됩니다."
empty-message="조회 조건에 해당하는 데이터 품질 검증 이력이 없습니다."
height="calc(100vh - 210px)"
/>
</PageLayout>
</BatchOperationsPageV2>
</template>