feat: Shadow Run Queue page (T06 template) — first KBX v60 page
Implement ShadowRunQueue.vue using KBX Foundation v60 components: - T06 Queue template for job list display - KbxScreenFrame wrapper with breadcrumb/title - KbxSummaryBar showing job statistics (running/completed/failed) - KbxTemplateStateBoundary for async state (loading/error/empty) - Filter bar (search, status dropdown) - Job items with progress bars, status tags, error messages - Actions per job (view details, export, retry) - Dark mode & responsive layout support New files: - features/shadow-run/pages/ShadowRunQueue.vue (page component) - features/shadow-run/composables/useShadowRunJobs.ts (data fetch) - features/shadow-run/types/index.ts (type definitions) Updated: - features/shadow-run/registry.ts (import ScreenDefinition from @kbx/contracts) Demo data: 3 sample jobs (running, completed, failed) with realistic states. Ready for API integration and production use. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Shadow Run Jobs Composable
|
||||
* Fetch and manage shadow run job list
|
||||
*/
|
||||
|
||||
import { ref, computed } from 'vue'
|
||||
import type { ShadowRunJob, ShadowRunJobFilter } from '../types'
|
||||
|
||||
export function useShadowRunJobs() {
|
||||
const jobs = ref<ShadowRunJob[]>([])
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const filter = ref<ShadowRunJobFilter>({})
|
||||
|
||||
// Mock data for demo — replace with actual API call
|
||||
const mockJobs: ShadowRunJob[] = [
|
||||
{
|
||||
jobId: '893',
|
||||
modelId: '00000000-0000-0000-0000-000000000001',
|
||||
modelName: 'Hawkeye-Alpha (v2.1)',
|
||||
status: 'running',
|
||||
windowStart: '2024-01-02',
|
||||
windowEnd: '2024-09-10',
|
||||
tradingDays: 252,
|
||||
startedAt: '2026-08-11T08:30:00Z',
|
||||
progress: 67,
|
||||
},
|
||||
{
|
||||
jobId: '892',
|
||||
modelId: '00000000-0000-0000-0000-000000000002',
|
||||
modelName: 'Falcon-Beta (v1.8)',
|
||||
status: 'completed',
|
||||
windowStart: '2024-01-02',
|
||||
windowEnd: '2024-09-10',
|
||||
tradingDays: 252,
|
||||
startedAt: '2026-08-05T10:15:00Z',
|
||||
completedAt: '2026-08-08T14:22:00Z',
|
||||
progress: 100,
|
||||
},
|
||||
{
|
||||
jobId: '891',
|
||||
modelId: '00000000-0000-0000-0000-000000000003',
|
||||
modelName: 'Eagle-Gamma (v3.0)',
|
||||
status: 'failed',
|
||||
windowStart: '2024-01-02',
|
||||
windowEnd: '2024-09-10',
|
||||
tradingDays: 252,
|
||||
startedAt: '2026-08-03T09:00:00Z',
|
||||
completedAt: '2026-08-03T12:45:00Z',
|
||||
progress: 0,
|
||||
errorMessage: 'Market data fetch timeout (KRX OpenAPI unavailable)',
|
||||
},
|
||||
]
|
||||
|
||||
const filteredJobs = computed(() => {
|
||||
let result = jobs.value
|
||||
|
||||
if (filter.value.status) {
|
||||
result = result.filter(j => j.status === filter.value.status)
|
||||
}
|
||||
|
||||
if (filter.value.modelId) {
|
||||
result = result.filter(j => j.modelId === filter.value.modelId)
|
||||
}
|
||||
|
||||
if (filter.value.search) {
|
||||
const q = filter.value.search.toLowerCase()
|
||||
result = result.filter(
|
||||
j => j.modelName.toLowerCase().includes(q) || j.jobId.includes(q)
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const statusStats = computed(() => ({
|
||||
running: jobs.value.filter(j => j.status === 'running').length,
|
||||
completed: jobs.value.filter(j => j.status === 'completed').length,
|
||||
failed: jobs.value.filter(j => j.status === 'failed').length,
|
||||
total: jobs.value.length,
|
||||
}))
|
||||
|
||||
async function fetchJobs() {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
// Simulate API call delay
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
jobs.value = mockJobs
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : 'Failed to fetch jobs'
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function setFilter(newFilter: ShadowRunJobFilter) {
|
||||
filter.value = newFilter
|
||||
}
|
||||
|
||||
return {
|
||||
jobs,
|
||||
filteredJobs,
|
||||
isLoading,
|
||||
error,
|
||||
statusStats,
|
||||
fetchJobs,
|
||||
setFilter,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user