mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-15 08:33:03 +00:00
Unify platform sub-page status toggles with overview compact pills
PlatformTableToolbar was rendering FilterButtonGroup with the default variant (heavy bg-surface-alt box, border-heavy active state, fixed blue active color) on every platform sub-page — Replication, Ceph, Backups, Mail Gateway, Docker Hosts/Services, K8s Clusters/Nodes/ Deployments, TrueNAS Systems, vSphere Hosts. The Proxmox / Docker overview WorkloadsFilter, by contrast, uses the compact variant with per-option tone, so "Running" goes green, "Stopped" goes red, etc. Switch the shared toolbar to compact and enrich the shared status filter option arrays (PLATFORM_STATUS_FILTER_OPTIONS, PLATFORM_HEALTH_FILTER_OPTIONS) with tone + leading status dot. Same treatment for the bespoke filter arrays on Replication (Healthy / Failed / Pending / Disabled), Ceph (Healthy / Warning / Critical), and Backups archives + tasks (Protected / Verified / Unverified, OK / Failed / Running). Backups archives and tasks also flip from default to compact variant since they bypass PlatformTableToolbar. Settings panel FilterButtonGroup usages stay default — different context, intentionally heavier.
This commit is contained in:
@@ -132,18 +132,20 @@ export function PlatformErrorState(props: {
|
||||
// 'running' vs 'online', 'stopped' vs 'offline') collapse to one chip set.
|
||||
export type PlatformResourceStatusFilter = 'all' | 'online' | 'degraded' | 'offline';
|
||||
|
||||
const statusDot = (className: string) => <span class={`h-2 w-2 rounded-full ${className}`} />;
|
||||
|
||||
export const PLATFORM_STATUS_FILTER_OPTIONS: FilterOption<PlatformResourceStatusFilter>[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'online', label: 'Online' },
|
||||
{ value: 'degraded', label: 'Degraded' },
|
||||
{ value: 'offline', label: 'Offline' },
|
||||
{ value: 'online', label: 'Online', tone: 'success', leading: statusDot('bg-emerald-500') },
|
||||
{ value: 'degraded', label: 'Degraded', tone: 'warning', leading: statusDot('bg-amber-500') },
|
||||
{ value: 'offline', label: 'Offline', tone: 'danger', leading: statusDot('bg-red-500') },
|
||||
];
|
||||
|
||||
export const PLATFORM_HEALTH_FILTER_OPTIONS: FilterOption<PlatformResourceStatusFilter>[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'online', label: 'Healthy' },
|
||||
{ value: 'degraded', label: 'Degraded' },
|
||||
{ value: 'offline', label: 'Offline' },
|
||||
{ value: 'online', label: 'Healthy', tone: 'success', leading: statusDot('bg-emerald-500') },
|
||||
{ value: 'degraded', label: 'Degraded', tone: 'warning', leading: statusDot('bg-amber-500') },
|
||||
{ value: 'offline', label: 'Offline', tone: 'danger', leading: statusDot('bg-red-500') },
|
||||
];
|
||||
|
||||
const ONLINE_STATUSES = new Set<ResourceStatus>(['online', 'running']);
|
||||
@@ -289,6 +291,7 @@ export function PlatformTableToolbar<T extends string | number>(props: {
|
||||
/>
|
||||
</div>
|
||||
<FilterButtonGroup
|
||||
variant="compact"
|
||||
options={props.statusOptions}
|
||||
value={props.status}
|
||||
onChange={props.onStatusChange}
|
||||
|
||||
@@ -147,18 +147,20 @@ async function fetchPVEBackups(): Promise<PVEBackupsPayload> {
|
||||
);
|
||||
}
|
||||
|
||||
const statusDot = (className: string) => <span class={`h-2 w-2 rounded-full ${className}`} />;
|
||||
|
||||
const ARCHIVE_STATUS_FILTERS: FilterOption<'all' | 'protected' | 'verified' | 'unverified'>[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'protected', label: 'Protected' },
|
||||
{ value: 'verified', label: 'Verified' },
|
||||
{ value: 'unverified', label: 'Unverified' },
|
||||
{ value: 'protected', label: 'Protected', tone: 'info', leading: statusDot('bg-blue-500') },
|
||||
{ value: 'verified', label: 'Verified', tone: 'success', leading: statusDot('bg-emerald-500') },
|
||||
{ value: 'unverified', label: 'Unverified', tone: 'warning', leading: statusDot('bg-amber-500') },
|
||||
];
|
||||
|
||||
const TASK_STATUS_FILTERS: FilterOption<'all' | 'ok' | 'failed' | 'running'>[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'ok', label: 'OK' },
|
||||
{ value: 'failed', label: 'Failed' },
|
||||
{ value: 'running', label: 'Running' },
|
||||
{ value: 'ok', label: 'OK', tone: 'success', leading: statusDot('bg-emerald-500') },
|
||||
{ value: 'failed', label: 'Failed', tone: 'danger', leading: statusDot('bg-red-500') },
|
||||
{ value: 'running', label: 'Running', tone: 'info', leading: statusDot('bg-blue-500') },
|
||||
];
|
||||
|
||||
export const ProxmoxBackupsTable: Component<{
|
||||
@@ -400,6 +402,7 @@ export const ProxmoxBackupsTable: Component<{
|
||||
</div>
|
||||
<Show when={tab() === 'archives'}>
|
||||
<FilterButtonGroup
|
||||
variant="compact"
|
||||
options={ARCHIVE_STATUS_FILTERS}
|
||||
value={archiveFilter()}
|
||||
onChange={setArchiveFilter}
|
||||
@@ -407,6 +410,7 @@ export const ProxmoxBackupsTable: Component<{
|
||||
</Show>
|
||||
<Show when={tab() === 'tasks'}>
|
||||
<FilterButtonGroup
|
||||
variant="compact"
|
||||
options={TASK_STATUS_FILTERS}
|
||||
value={taskFilter()}
|
||||
onChange={setTaskFilter}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from 'solid-js';
|
||||
import { Card } from '@/components/shared/Card';
|
||||
import { EmptyState } from '@/components/shared/EmptyState';
|
||||
import type { FilterOption } from '@/components/shared/FilterButtonGroup';
|
||||
import { StatusDot } from '@/components/shared/StatusDot';
|
||||
import type { StatusIndicatorVariant } from '@/utils/status';
|
||||
import { TableCard } from '@/components/shared/TableCard';
|
||||
@@ -46,12 +47,14 @@ import { ProxmoxCephClusterDrawer } from './ProxmoxCephClusterDrawer';
|
||||
|
||||
type CephStatusFilter = 'all' | 'healthy' | 'warning' | 'critical';
|
||||
|
||||
const STATUS_FILTER_OPTIONS = [
|
||||
const statusDot = (className: string) => <span class={`h-2 w-2 rounded-full ${className}`} />;
|
||||
|
||||
const STATUS_FILTER_OPTIONS: FilterOption<CephStatusFilter>[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'healthy', label: 'Healthy' },
|
||||
{ value: 'warning', label: 'Warning' },
|
||||
{ value: 'critical', label: 'Critical' },
|
||||
] satisfies Array<{ value: CephStatusFilter; label: string }>;
|
||||
{ value: 'healthy', label: 'Healthy', tone: 'success', leading: statusDot('bg-emerald-500') },
|
||||
{ value: 'warning', label: 'Warning', tone: 'warning', leading: statusDot('bg-amber-500') },
|
||||
{ value: 'critical', label: 'Critical', tone: 'danger', leading: statusDot('bg-red-500') },
|
||||
];
|
||||
|
||||
function classify(resource: Resource): CephStatusFilter {
|
||||
const raw = (resource.ceph?.healthStatus ?? resource.status ?? '').toUpperCase();
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from '@/components/shared/Table';
|
||||
import { apiFetch } from '@/utils/apiClient';
|
||||
import { formatRelativeTime } from '@/utils/format';
|
||||
import type { FilterOption } from '@/components/shared/FilterButtonGroup';
|
||||
import {
|
||||
PLATFORM_TABLE_BODY_CLASS,
|
||||
PLATFORM_TABLE_CARD_CLASS,
|
||||
@@ -42,13 +43,15 @@ import type { ReplicationJob, ReplicationJobsResponse } from '@/types/api';
|
||||
|
||||
type ReplicationStatusFilter = 'all' | 'healthy' | 'failed' | 'pending' | 'disabled';
|
||||
|
||||
const STATUS_FILTER_OPTIONS = [
|
||||
const statusDot = (className: string) => <span class={`h-2 w-2 rounded-full ${className}`} />;
|
||||
|
||||
const STATUS_FILTER_OPTIONS: FilterOption<ReplicationStatusFilter>[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'healthy', label: 'Healthy' },
|
||||
{ value: 'failed', label: 'Failed' },
|
||||
{ value: 'pending', label: 'Pending' },
|
||||
{ value: 'disabled', label: 'Disabled' },
|
||||
] satisfies Array<{ value: ReplicationStatusFilter; label: string }>;
|
||||
{ value: 'healthy', label: 'Healthy', tone: 'success', leading: statusDot('bg-emerald-500') },
|
||||
{ value: 'failed', label: 'Failed', tone: 'danger', leading: statusDot('bg-red-500') },
|
||||
{ value: 'pending', label: 'Pending', tone: 'warning', leading: statusDot('bg-amber-500') },
|
||||
{ value: 'disabled', label: 'Disabled', tone: 'muted', leading: statusDot('bg-slate-400') },
|
||||
];
|
||||
|
||||
interface ReplicationStatusIndicator {
|
||||
variant: StatusIndicatorVariant;
|
||||
|
||||
Reference in New Issue
Block a user