Both are named for what they do rather than the tool underneath, the way Services is. Old links and saved rail orders still land in the right place. The Go packages and their endpoints keep their names, with a pointer in each to what the UI calls them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
53 lines
2.1 KiB
TypeScript
53 lines
2.1 KiB
TypeScript
// The order of the pages in the rail is the account's: kept here for instant use and saved
|
|
// with the UI prefs, so it follows the login. Pages the saved order doesn't name (added since)
|
|
// go after the ones it does, in their built-in order.
|
|
import { useEffect, useState } from 'react'
|
|
|
|
const KEY = 'hamlab.rail'
|
|
|
|
// Pages that were renamed, so an order saved before it still puts the page where it was put:
|
|
// Top became Processes, Space became Usage, Cron became Jobs. A page that was removed
|
|
// outright (Dashboard) is not here — orderBy drops ids that match no page.
|
|
const RENAMED: Record<string, string> = { top: 'procs', space: 'usage', cron: 'jobs' }
|
|
|
|
export function getRailOrder(): string[] {
|
|
let raw: unknown
|
|
try { raw = JSON.parse(localStorage.getItem(KEY) ?? '[]') } catch { return [] }
|
|
if (!Array.isArray(raw)) return []
|
|
const seen = new Set<string>()
|
|
const out: string[] = []
|
|
for (const x of raw) {
|
|
if (typeof x !== 'string') continue
|
|
const id = RENAMED[x] ?? x
|
|
if (seen.has(id)) continue // a merged page keeps the earlier of the two slots
|
|
seen.add(id)
|
|
out.push(id)
|
|
}
|
|
return out
|
|
}
|
|
|
|
export function setRailOrder(ids: string[], save = true) {
|
|
localStorage.setItem(KEY, JSON.stringify(ids))
|
|
window.dispatchEvent(new Event('hamlab:rail'))
|
|
if (save) import('./accents').then((m) => m.savePrefs())
|
|
}
|
|
|
|
export function resetRailOrder() { localStorage.removeItem(KEY); window.dispatchEvent(new Event('hamlab:rail')); import('./accents').then((m) => m.savePrefs()) }
|
|
|
|
export function useRailOrder(): string[] {
|
|
const [order, setOrder] = useState(getRailOrder)
|
|
useEffect(() => {
|
|
const h = () => setOrder(getRailOrder())
|
|
window.addEventListener('hamlab:rail', h)
|
|
return () => window.removeEventListener('hamlab:rail', h)
|
|
}, [])
|
|
return order
|
|
}
|
|
|
|
export function orderBy<T extends { id: string }>(items: T[], order: string[]): T[] {
|
|
const by = new Map(items.map((g) => [g.id, g]))
|
|
const named = order.map((id) => by.get(id)).filter((g): g is T => !!g)
|
|
const seen = new Set(named.map((g) => g.id))
|
|
return [...named, ...items.filter((g) => !seen.has(g.id))]
|
|
}
|