Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 45 additions & 8 deletions lib/iris/dashboard/src/components/controller/JobsTab.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { RouterLink } from 'vue-router'
import { RouterLink, useRoute, useRouter } from 'vue-router'
import { controllerRpcCall, useControllerRpc } from '@/composables/useRpc'
import { useAutoRefresh } from '@/composables/useAutoRefresh'
import { stateToName, stateDisplayName } from '@/types/status'
Expand All @@ -24,6 +24,9 @@ const SORT_FIELD_MAP: Record<string, string> = {
type SortField = 'date' | 'name' | 'state' | 'failures' | 'preemptions'
type SortDir = 'asc' | 'desc'

const SORT_FIELDS: SortField[] = ['date', 'name', 'state', 'failures', 'preemptions']
const SORT_DIRS: SortDir[] = ['asc', 'desc']

const copiedJob = ref<string | null>(null)

async function copyJobName(name: string) {
Expand All @@ -32,16 +35,36 @@ async function copyJobName(name: string) {
setTimeout(() => { copiedJob.value = null }, 1500)
}

const route = useRoute()
const router = useRouter()

const EXPANDED_JOBS_KEY = 'iris.controller.expandedJobs'

// -- State --
// -- State (hydrated from URL query params) --

/** Safely extract a single string from a Vue Router query value (string | string[] | null). */
function queryStr(v: string | string[] | null | undefined): string {
if (Array.isArray(v)) return v[0] ?? ''
return v ?? ''
}

const page = ref(0)
const sortField = ref<SortField>('date')
const sortDir = ref<SortDir>('desc')
const nameFilter = ref('')
const localFilter = ref('')
const stateFilter = ref('')
function parseSort(v: string): SortField {
return SORT_FIELDS.includes(v as SortField) ? (v as SortField) : 'date'
}
function parseDir(v: string): SortDir {
return SORT_DIRS.includes(v as SortDir) ? (v as SortDir) : 'desc'
}
function parsePage(v: string): number {
const n = Number(v)
return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0
}

const page = ref(parsePage(queryStr(route.query.page)))
const sortField = ref<SortField>(parseSort(queryStr(route.query.sort)))
const sortDir = ref<SortDir>(parseDir(queryStr(route.query.dir)))
const nameFilter = ref(queryStr(route.query.name))
const localFilter = ref(queryStr(route.query.name))
const stateFilter = ref(queryStr(route.query.state))
const expandedJobs = ref<Set<string>>(loadExpandedJobs())
const childJobsByParent = ref<Map<string, JobStatus[]>>(new Map())
const loadingChildJobs = ref<Set<string>>(new Set())
Expand Down Expand Up @@ -134,6 +157,20 @@ watch(stateFilter, () => {
page.value = 0
})

// Sync filter/sort/page state into the URL so back-button and link sharing work.
watch([page, sortField, sortDir, nameFilter, stateFilter], () => {
router.replace({
query: {
...route.query,
sort: sortField.value !== 'date' ? sortField.value : undefined,
dir: sortDir.value !== 'desc' ? sortDir.value : undefined,
page: page.value !== 0 ? String(page.value) : undefined,
name: nameFilter.value || undefined,
state: stateFilter.value || undefined,
},
})
})

// -- Job tree (lazy-loaded children) --

const flattenedJobs = computed(() => flattenLoadedJobTree(jobs.value, childJobsByParent.value, expandedJobs.value))
Expand Down
Loading