mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-14 05:18:54 +00:00
Every tab row scrolls sideways instead of being clipped by its card. On phones this hid tabs, such as the Config change helper's last forms at 360-390px. The login button has a larger touch target, and long names are truncated with a tooltip. Loading and empty states: - Skeletons show only while a selection has no data yet, never over data already on screen during a refresh. - Empty states name the next step to take. - Error states offer a Retry button that shows progress, and each card keeps its own error. - Toasts confirm copies and key generation, and report background failures once per streak through polite and assertive live regions. Other changes: - The access matrix switches between Models, MCP servers and Agents, and says "none configured" only once its data has loaded. - Home ranks the top 5 users by cost, requests or tokens. It requests only the needed metrics and warns before a range exceeds the server's key cap. - MCP and agent deep links accept a bare target name. admin_assets_gen.go is regenerated. Claude-Session: https://claude.ai/code/session_01V5jYJNxpDFdJkyyExpADrv
44 lines
2.4 KiB
TypeScript
44 lines
2.4 KiB
TypeScript
/**
|
|
* LoadState is the four states any data-driven view in this panel
|
|
* renders in place of its real content — the single vocabulary
|
|
* EmptyState.vue/ErrorState.vue/the Skeleton* components all key off of,
|
|
* so every page decides "skeleton vs error vs empty vs ready" the same
|
|
* way instead of each growing its own ad-hoc v-if chain.
|
|
*/
|
|
export type LoadState = 'skeleton' | 'error' | 'empty' | 'ready'
|
|
|
|
/** LoadStateInput is loadState's own three signals — every store in this panel already exposes something that maps onto these (a `loading`/`lastUpdated` flag, a data field's null-ness or length, an `error` string). */
|
|
export interface LoadStateInput {
|
|
/** Whether a fetch for the CURRENT selection is in flight (or has never yet completed) — a background refresh of data already on screen does not count (see hasData's own precedence below). */
|
|
loading: boolean
|
|
/** Whether there is currently displayable data for the CURRENT selection — false right after a filter/selection change clears the previous selection's data, not just on a cold first load. */
|
|
hasData: boolean
|
|
/** The store's current error message, '' when there is none. */
|
|
error: string
|
|
}
|
|
|
|
/**
|
|
* loadState decides which of the four LoadState values a view should
|
|
* render, from the three signals above. Precedence, highest first:
|
|
*
|
|
* 1. 'ready' whenever `hasData` is true — even alongside a non-empty
|
|
* `error` (a background poll just failed but the PREVIOUS successful
|
|
* result is still on screen): states-plan.md item 1's own rule is
|
|
* that a background refresh must never replace rendered data with a
|
|
* skeleton, and a background failure must never blank it into an
|
|
* error state either — that case surfaces via a toast instead
|
|
* (stores/toasts.ts), not by this helper switching states.
|
|
* 2. 'error' when there is no data and `error` is set — the first fetch
|
|
* for this selection failed outright, nothing to show underneath it.
|
|
* 3. 'skeleton' when there is no data, no error, and `loading` is true —
|
|
* first load, or a filter/selection change that cleared the previous
|
|
* selection's own data and kicked off a fresh fetch.
|
|
* 4. 'empty' otherwise — not loading, no error, genuinely no data.
|
|
*/
|
|
export function loadState({ loading, hasData, error }: LoadStateInput): LoadState {
|
|
if (hasData) return 'ready'
|
|
if (error) return 'error'
|
|
if (loading) return 'skeleton'
|
|
return 'empty'
|
|
}
|