From a6af6e4b985e09e7dbf7f4466370cfdb005aac3f Mon Sep 17 00:00:00 2001 From: "Jan-Peter v. Hunnius" Date: Sat, 6 Jun 2026 10:20:43 +0200 Subject: [PATCH] feat: add run deletion, session reopen, and feed merging UI --- src/api/client.ts | 9 +++++ src/stores/sessions.ts | 36 +++++++++++++++++++ src/types/index.ts | 1 + src/views/SessionDetailView.vue | 61 +++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+) diff --git a/src/api/client.ts b/src/api/client.ts index c46563c..c5918eb 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -88,6 +88,15 @@ export async function stopRun(sessionId: string, runId: string): Promise { + await api.delete(`/sessions/${sessionId}/runs/${runId}`) +} + +export async function reopenSession(id: string): Promise { + const res = await api.post(`/sessions/${id}/reopen`) + return res.data +} + export async function completeSession(id: string): Promise { const res = await api.post(`/sessions/${id}/complete`) return res.data diff --git a/src/stores/sessions.ts b/src/stores/sessions.ts index 606a40f..fd1c62b 100644 --- a/src/stores/sessions.ts +++ b/src/stores/sessions.ts @@ -119,6 +119,40 @@ export const useSessionsStore = defineStore('sessions', () => { } } + async function deleteRun(sessionId: string, runId: string): Promise { + try { + await api.deleteRun(sessionId, runId) + if (currentSession.value?.id === sessionId) { + currentSession.value.runs = currentSession.value.runs.filter((r) => r.id !== runId) + const sIdx = sessions.value.findIndex((s) => s.id === sessionId) + if (sIdx !== -1) { + sessions.value[sIdx] = { ...sessions.value[sIdx], runs: [...currentSession.value.runs] } + } + } + return true + } catch (e: unknown) { + error.value = e instanceof Error ? e.message : 'Failed to delete run' + return false + } + } + + async function reopenSession(id: string): Promise { + try { + const session = await api.reopenSession(id) + const idx = sessions.value.findIndex((s) => s.id === id) + if (idx !== -1) { + sessions.value[idx] = session + } + if (currentSession.value?.id === id) { + currentSession.value = session + } + return session + } catch (e: unknown) { + error.value = e instanceof Error ? e.message : 'Failed to reopen session' + return null + } + } + return { sessions, currentSession, @@ -130,7 +164,9 @@ export const useSessionsStore = defineStore('sessions', () => { deleteSession, startRun, stopRun, + deleteRun, completeSession, + reopenSession, reAnalyze, } }) diff --git a/src/types/index.ts b/src/types/index.ts index e1d4b56..7edd1ec 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -112,6 +112,7 @@ export interface CreateSessionRequest { export interface StartRunRequest { feed_name: string label?: string | null + merge_feeds?: string[] } // ── Use Cases ── diff --git a/src/views/SessionDetailView.vue b/src/views/SessionDetailView.vue index c121443..a1e11f7 100644 --- a/src/views/SessionDetailView.vue +++ b/src/views/SessionDetailView.vue @@ -16,12 +16,18 @@ const store = useSessionsStore() const showStartRun = ref(false) const runFeedName = ref('ksudp') const runLabel = ref('') +const runMergeEnabled = ref(false) +const runMergeFeeds = ref([]) const sessionId = computed(() => props.id ?? (route.params.id as string)) const session = computed(() => store.currentSession) const feedOptions = ['ksudp', 'telemetry_tool', 'pcars'] +const mergeFeedOptions = computed(() => { + return feedOptions.filter((f) => f !== runFeedName.value) +}) + const hasActiveRun = computed(() => { return session.value?.runs.some((r) => r.status === 'recording') ?? false }) @@ -61,10 +67,13 @@ async function handleStartRun() { const run = await store.startRun(sessionId.value, { feed_name: runFeedName.value, label: runLabel.value || undefined, + merge_feeds: runMergeEnabled.value && runMergeFeeds.value.length > 0 ? runMergeFeeds.value : undefined, }) if (run) { showStartRun.value = false runLabel.value = '' + runMergeEnabled.value = false + runMergeFeeds.value = [] } } @@ -102,6 +111,16 @@ async function handleDelete() { router.push('/sessions') } } + +async function handleDeleteRun(runId: string, runLabel: string) { + if (confirm(`Delete run "${runLabel || 'Untitled Run'}"?`)) { + await store.deleteRun(sessionId.value, runId) + } +} + +async function handleReopen() { + await store.reopenSession(sessionId.value) +}