8974d6087c
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>
112 lines
2.8 KiB
TypeScript
112 lines
2.8 KiB
TypeScript
/**
|
|
* 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,
|
|
}
|
|
}
|