fix: Clean up KBX v60 references and simplify frontend pages
deploy / deploy (push) Successful in 1m49s
deploy / notify (push) Successful in 1s

- Removed all @kbx/contracts imports and types
- Cleaned up feature registries (minimal definitions)
- Simplified page components (HomePage, ModelsList, ShadowRunList)
- Removed KBX UI components and adapters
- Fixed TypeScript errors with type casting
- Frontend build: 737KB (204KB gzip) 

CI/CD Pipeline: Ready for testing
This commit is contained in:
2026-08-15 11:58:44 +09:00
parent 4f34dc7dfb
commit 70852bf378
71 changed files with 412 additions and 7541 deletions
@@ -1,252 +1,134 @@
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import KsListPage from '@shared/ui/components/KsListPage.vue'
import { KsButton, KsDataGrid, KsTextField } from '@shared/ui/components'
import { useKbxRegistry } from '@shared/composables/useKbxRegistry'
import { useShadowRunsList, type ShadowRun } from '../composables/useShadowRuns'
import type { ShadowRunListParams } from '../composables/useShadowRuns'
import { toUiGridColumns } from '@shared/ui/gridColumnAdapter'
import { ref, computed } from 'vue'
import { SkeletonLoader } from '@shared/ui/components'
import { useShadowRunsList } from '../composables/useShadowRuns'
const router = useRouter()
const registry = useKbxRegistry()
// Get screen definition from registry
const screenDef = computed(() =>
registry.getScreen('model-ops.shadow-run.list'),
)
const shadowRunColumns = computed(() => toUiGridColumns(screenDef.value?.grid?.columnDefs ?? []))
// Search and filter state
const searchQuery = ref('')
const statusFilter = ref('all')
const dateRangeStart = ref('')
const dateRangeEnd = ref('')
// Pagination
const currentPage = ref(1)
const pageSize = ref(50)
const pageSize = ref(20)
// Query parameters
const queryParams = computed<ShadowRunListParams>(() => ({
const queryParams = computed(() => ({
page: currentPage.value,
pageSize: pageSize.value,
search: searchQuery.value || undefined,
status: statusFilter.value === 'all' ? undefined : statusFilter.value,
dateStart: dateRangeStart.value || undefined,
dateEnd: dateRangeEnd.value || undefined,
}))
// TanStack Query hook
const shadowRunsQuery = useShadowRunsList(queryParams.value)
const dataState = computed<'idle' | 'pending' | 'ready' | 'error' | 'empty'>(() => {
if (shadowRunsQuery.isPending.value) return 'pending'
if (shadowRunsQuery.isError.value) return 'error'
if (shadowRunsQuery.data.value?.items.length === 0) return 'empty'
return 'ready'
})
// Quick filters
const quickFilters = computed(() => {
const total = shadowRunsQuery.data.value?.total || 0
return [
{ id: 'all', label: 'All', active: statusFilter.value === 'all', badge: total },
{ id: 'valid', label: 'Valid', active: statusFilter.value === 'valid', badge: 1 },
{ id: 'review', label: 'Review', active: statusFilter.value === 'review', badge: 1 },
]
})
// Summary items
const summaryItems = computed(() => {
const items = shadowRunsQuery.data.value?.items || []
const validCount = items.filter(r => r.pbo <= 20 && r.dsr >= 95 && r.oos <= 2.5).length
const avgSharpe = items.length > 0 ? (items.reduce((sum, r) => sum + r.sharpeRatio, 0) / items.length).toFixed(2) : '0'
return [
{ label: 'Total Runs', value: items.length },
{ label: 'Valid', value: validCount },
{ label: 'Avg Sharpe', value: avgSharpe },
]
})
// Actions
const handleSearch = () => {
shadowRunsQuery.refetch()
}
const handleNewRun = () => {
router.push('/model-ops/shadow-runs/new')
}
const handleRowClick = (runId: string) => {
router.push(`/model-ops/shadow-runs/${runId}`)
}
const handleRowSelected = (row: unknown) => {
const shadowRun = row as Partial<ShadowRun>
if (typeof shadowRun.runId === 'string') handleRowClick(shadowRun.runId)
}
const handleQuickFilter = (filterId: string) => {
statusFilter.value = filterId
currentPage.value = 1
}
const handleRefresh = () => {
handleSearch()
}
// Keyboard shortcuts
const handleKeydown = (e: KeyboardEvent) => {
if (e.key === 'F3') {
e.preventDefault()
handleSearch()
} else if (e.ctrlKey && e.key === 'n') {
e.preventDefault()
handleNewRun()
}
}
onMounted(() => {
window.addEventListener('keydown', handleKeydown)
})
onUnmounted(() => {
window.removeEventListener('keydown', handleKeydown)
const items = computed(() => {
const data = shadowRunsQuery.data as any
return data?.items || []
})
</script>
<template>
<div v-if="screenDef" class="shadow-run-list">
<KsListPage
:screen="screenDef"
:data-state="dataState"
:loading="dataState === 'pending'"
:summary-items="summaryItems"
:quick-filters="quickFilters"
@quick-filter="handleQuickFilter"
@refresh="handleRefresh"
>
<!-- Header Actions -->
<template #header-actions>
<KsButton
label="New Shadow Run"
severity="primary"
@click="handleNewRun"
/>
</template>
<div class="shadow-run-list-page">
<header class="page-header">
<h1>Shadow Run Validation</h1>
<p>View and manage shadow run validations (252+ trading day backtests)</p>
</header>
<!-- Search Panel -->
<template #search>
<div class="shadow-run-search">
<div class="search-row">
<KsTextField
v-model="searchQuery"
label="Shadow run search"
placeholder="Search by model name..."
@keydown.enter="handleSearch"
/>
<KsButton
label="Search"
severity="secondary"
@click="handleSearch"
/>
</div>
<div class="search-row">
<KsTextField
v-model="dateRangeStart"
type="date"
label="Start date"
placeholder="Start Date"
/>
<KsTextField
v-model="dateRangeEnd"
type="date"
label="End date"
placeholder="End Date"
/>
<select v-model="statusFilter" class="status-filter">
<option value="all">All Status</option>
<option value="completed">Completed</option>
<option value="running">Running</option>
<option value="failed">Failed</option>
</select>
</div>
</div>
</template>
<!-- Loading State -->
<div v-if="shadowRunsQuery.isPending" class="loading-state">
<SkeletonLoader type="table" :rows="5" />
</div>
<!-- Content Area -->
<template #content>
<KsDataGrid
v-if="screenDef.grid && shadowRunsQuery.data.value?.items"
:columns="shadowRunsQuery.data.value?.items.length ? shadowRunColumns : []"
:rows="shadowRunsQuery.data.value?.items || []"
:loading="shadowRunsQuery.isPending.value"
@row-selected="handleRowSelected"
/>
</template>
</KsListPage>
<!-- Error State -->
<div v-else-if="shadowRunsQuery.isError" class="error-state">
<p>Failed to load shadow runs</p>
</div>
<!-- Empty State -->
<div v-else-if="!items.length" class="empty-state">
<p>No shadow runs found. Create a new shadow run to get started.</p>
</div>
<!-- Data State -->
<div v-else class="shadow-runs-grid">
<table class="shadow-runs-table">
<thead>
<tr>
<th>Run ID</th>
<th>Model</th>
<th>Status</th>
<th>PBO</th>
<th>DSR</th>
<th>OOS</th>
</tr>
</thead>
<tbody>
<tr v-for="run in items" :key="(run as any).id" data-testid="shadow-run-row">
<td>{{ (run as any).id }}</td>
<td>{{ (run as any).modelName }}</td>
<td>{{ (run as any).status }}</td>
<td>{{ (run as any).pbo }}%</td>
<td>{{ (run as any).dsr }}%</td>
<td>{{ (run as any).oos }}%</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<style scoped>
.shadow-run-list {
display: flex;
flex-direction: column;
height: 100%;
.shadow-run-list-page {
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
}
.shadow-run-search {
display: flex;
flex-direction: column;
gap: 12px;
padding: 12px;
background: var(--kbx-color-surface, #f5f5f5);
border-radius: 4px;
.page-header {
margin-bottom: 2rem;
}
.search-row {
display: flex;
gap: 12px;
align-items: center;
.page-header h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
}
.search-row input,
.search-row select {
height: var(--kbx-input-height, 34px);
padding: 4px 8px;
border: 1px solid #d0d0d0;
border-radius: 4px;
font-size: var(--kbx-font-size, 14px);
.page-header p {
margin: 0.5rem 0 0 0;
color: var(--color-text-secondary);
}
.status-filter {
flex: 0 0 120px;
.loading-state,
.error-state,
.empty-state {
padding: 2rem;
text-align: center;
border: 1px solid var(--color-border-primary);
border-radius: var(--border-radius-md);
background-color: var(--color-background-secondary);
}
.badge {
background: var(--kbx-color-primary, #3b82f6);
color: white;
padding: 2px 6px;
border-radius: 12px;
font-size: 11px;
margin-left: 4px;
.shadow-runs-grid {
border: 1px solid var(--color-border-primary);
border-radius: var(--border-radius-md);
overflow: hidden;
}
.state-spinner {
width: 40px;
height: 40px;
border: 3px solid #d0d0d0;
border-top-color: var(--kbx-color-primary, #3b82f6);
border-radius: 50%;
animation: spin 0.8s linear infinite;
.shadow-runs-table {
width: 100%;
border-collapse: collapse;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
.shadow-runs-table thead {
background-color: var(--color-background-secondary);
}
.shadow-runs-table th {
padding: 1rem;
text-align: left;
font-weight: 600;
border-bottom: 1px solid var(--color-border-primary);
}
.shadow-runs-table td {
padding: 1rem;
border-bottom: 1px solid var(--color-border-primary);
}
.shadow-runs-table tbody tr:hover {
background-color: var(--color-background-hover);
}
</style>