Skip to content
Merged
Show file tree
Hide file tree
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
17 changes: 15 additions & 2 deletions src/component-library/features/search/DIDSearchPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,26 @@ export const DIDSearchPanel = (props: SearchPanelProps) => {

const onScopeArrowDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'ArrowRight') {
nameInputRef.current?.focus();
const input = event.currentTarget;
const cursorPosition = input.selectionStart;
const textLength = input.value.length;

// Only switch to name input if cursor is at the end
if (cursorPosition === textLength) {
nameInputRef.current?.focus();
}
}
};

const onNameArrowDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'ArrowLeft') {
scopeInputRef.current?.focus();
const input = event.currentTarget;
const cursorPosition = input.selectionStart;

// Only switch to scope input if cursor is at the beginning
if (cursorPosition === 0) {
scopeInputRef.current?.focus();
}
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import useTableStreaming from '@/lib/infrastructure/hooks/useTableStreaming';
import { formatDate, formatSeconds } from '@/component-library/features/utils/text-formatters';
import { RuleStateBadge } from '@/component-library/features/badges/Rule/RuleStateBadge';
import { NullBadge } from '@/component-library/features/badges/NullBadge';
import { ruleActivityComparator } from '@/lib/core/utils/rule-sorting-utils';
import { ruleActivityComparator, remainingLifetimeComparator, ruleStateComparator } from '@/lib/core/utils/rule-sorting-utils';

type DetailsDIDRulesTableProps = {
streamingHook: UseStreamReader<DIDRulesViewModel>;
Expand Down Expand Up @@ -76,6 +76,8 @@ export const DetailsDIDRulesTable = (props: DetailsDIDRulesTableProps) => {
},
filter: true,
filterParams: buildDiscreteFilterParams(Object.values(RuleStateDisplayNames), Object.values(RuleState)),
sortable: true,
comparator: ruleStateComparator,
},
// TODO: minified header with a tooltip
{
Expand Down Expand Up @@ -125,16 +127,18 @@ export const DetailsDIDRulesTable = (props: DetailsDIDRulesTableProps) => {
minWidth: 120,
width: 140,
cellRenderer: NullableRemainingLifetime,
sortable: true,
comparator: remainingLifetimeComparator,
},
]);

const onGridReady = (event: GridReadyEvent) => {
props.onGridReady(event);
// Apply default sort to prioritize stuck rules
// Apply default sort to prioritize error/stuck rules
event.api.applyColumnState({
state: [
{
colId: 'locks_stuck_cnt',
colId: 'state',
sort: 'desc',
},
],
Expand Down
18 changes: 17 additions & 1 deletion src/component-library/pages/Rule/details/DetailsRuleLocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { FTSLinkViewModel } from '@/lib/infrastructure/data/view-model/request';
import { useToast } from '@/lib/infrastructure/hooks/useToast';
import { LoadingSpinner } from '@/component-library/atoms/loading/LoadingSpinner';
import { HiExternalLink } from 'react-icons/hi';
import { lockStateComparator } from '@/lib/core/utils/rule-sorting-utils';

type DetailsRuleLocksTableProps = {
streamingHook: UseStreamReader<ListRuleReplicaLockStatesViewModel>;
Expand Down Expand Up @@ -150,6 +151,8 @@ const DetailsRuleLocksTable = (props: DetailsRuleLocksTableProps) => {
},
filter: true,
filterParams: buildDiscreteFilterParams(Object.values(LockStateDisplayNames), Object.values(LockState)),
sortable: true,
comparator: lockStateComparator,
},
{
headerName: 'FTS Monitoring',
Expand All @@ -159,7 +162,20 @@ const DetailsRuleLocksTable = (props: DetailsRuleLocksTableProps) => {
},
]);

return <StreamedTable columnDefs={columnDefs} tableRef={tableRef} {...props} />;
const onGridReady = (event: GridReadyEvent) => {
props.onGridReady(event);
// Apply default sort to prioritize error/stuck locks
event.api.applyColumnState({
state: [
{
colId: 'state',
sort: 'desc',
},
],
});
};

return <StreamedTable columnDefs={columnDefs} tableRef={tableRef} {...props} onGridReady={onGridReady} />;
};

export const DetailsRuleLocks = ({ id }: { id: string }) => {
Expand Down
8 changes: 5 additions & 3 deletions src/component-library/pages/Rule/list/ListRuleTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { formatDate, formatSeconds } from '@/component-library/features/utils/te
import { RuleStateBadge } from '@/component-library/features/badges/Rule/RuleStateBadge';
import { RuleState } from '@/lib/core/entity/rucio';
import { NullBadge } from '@/component-library/features/badges/NullBadge';
import { ruleActivityComparator, remainingLifetimeComparator } from '@/lib/core/utils/rule-sorting-utils';
import { ruleActivityComparator, remainingLifetimeComparator, ruleStateComparator } from '@/lib/core/utils/rule-sorting-utils';

type ListRuleTableProps = {
streamingHook: UseStreamReader<RuleViewModel>;
Expand Down Expand Up @@ -96,6 +96,8 @@ export const ListRuleTable = (props: ListRuleTableProps) => {
},
filter: true,
filterParams: buildDiscreteFilterParams(Object.values(RuleStateDisplayNames), Object.values(RuleState)),
sortable: true,
comparator: ruleStateComparator,
},
// TODO: minified header with a tooltip
{
Expand Down Expand Up @@ -153,11 +155,11 @@ export const ListRuleTable = (props: ListRuleTableProps) => {

const onGridReady = (event: GridReadyEvent) => {
props.onGridReady(event);
// Apply default sort to prioritize stuck rules
// Apply default sort to prioritize error/stuck rules
event.api.applyColumnState({
state: [
{
colId: 'locks_stuck_cnt',
colId: 'state',
sort: 'desc',
},
],
Expand Down
54 changes: 54 additions & 0 deletions src/lib/core/utils/rule-sorting-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,57 @@ export function remainingLifetimeComparator(valueA: number | null | undefined, v
// Numeric comparison (values are in seconds)
return valueA - valueB;
}

/**
* Get priority score for lock states
* Higher scores indicate higher priority (more urgent states)
*/
function getLockStatePriority(state: string): number {
const stateLower = state.toLowerCase();

if (stateLower.includes('error')) return 4;
if (stateLower === 'stuck') return 3;
if (stateLower === 'replicating') return 2;
if (stateLower === 'ok') return 1;
return 0; // unknown or other states
}

/**
* Comparator function for ag-Grid that sorts lock states by priority
* Order: ERROR > STUCK > REPLICATING > OK > UNKNOWN
*/
export function lockStateComparator(valueA: string, valueB: string): number {
const priorityA = getLockStatePriority(valueA);
const priorityB = getLockStatePriority(valueB);

// Higher priority first
return priorityB - priorityA;
}

/**
* Get priority score for rule states
* Higher scores indicate higher priority (more urgent states)
*/
function getRuleStatePriority(state: string): number {
const stateLower = state.toLowerCase();

if (stateLower === 'stuck') return 6;
if (stateLower === 'suspended') return 5;
if (stateLower === 'replicating') return 4;
if (stateLower === 'waiting_approval') return 3;
if (stateLower === 'inject') return 2;
if (stateLower === 'ok') return 1;
return 0; // unknown or other states
}

/**
* Comparator function for ag-Grid that sorts rule states by priority
* Order: STUCK > SUSPENDED > REPLICATING > WAITING_APPROVAL > INJECT > OK > UNKNOWN
*/
export function ruleStateComparator(valueA: string, valueB: string): number {
const priorityA = getRuleStatePriority(valueA);
const priorityB = getRuleStatePriority(valueB);

// Higher priority first
return priorityB - priorityA;
}
Loading