전략평가 목록 정렬

This commit is contained in:
Macbook
2026-06-23 00:49:18 +09:00
parent c63775bba6
commit 32be015b49
3 changed files with 130 additions and 2 deletions
@@ -0,0 +1,81 @@
import type { StrategyDto } from './backendApi';
import { repairUtf8Mojibake } from './textEncoding';
export type StrategyListSortField = 'name' | 'createdAt';
export type StrategyListSortDir = 'asc' | 'desc';
export interface StrategyListSort {
field: StrategyListSortField;
dir: StrategyListSortDir;
}
const STORAGE_KEY = 'seval-strategy-list-sort';
const DEFAULT_SORT: StrategyListSort = { field: 'createdAt', dir: 'desc' };
export function loadStrategyListSort(): StrategyListSort {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return DEFAULT_SORT;
const parsed = JSON.parse(raw) as Partial<StrategyListSort>;
const field = parsed.field === 'name' || parsed.field === 'createdAt' ? parsed.field : DEFAULT_SORT.field;
const dir = parsed.dir === 'asc' || parsed.dir === 'desc' ? parsed.dir : DEFAULT_SORT.dir;
return { field, dir };
} catch {
return DEFAULT_SORT;
}
}
export function saveStrategyListSort(sort: StrategyListSort): void {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(sort));
} catch {
/* ignore quota */
}
}
/** 같은 필드 재클릭 시 방향 토글, 다른 필드는 해당 필드 기본 방향 */
export function toggleStrategyListSort(
prev: StrategyListSort,
field: StrategyListSortField,
): StrategyListSort {
if (prev.field !== field) {
return field === 'name'
? { field: 'name', dir: 'asc' }
: { field: 'createdAt', dir: 'desc' };
}
return { field, dir: prev.dir === 'asc' ? 'desc' : 'asc' };
}
function strategyCreatedTimeMs(s: StrategyDto): number {
if (s.createdAt) {
const t = Date.parse(s.createdAt);
if (Number.isFinite(t)) return t;
}
return s.id ?? 0;
}
export function sortStrategies(
list: StrategyDto[],
sort: StrategyListSort,
): StrategyDto[] {
const mul = sort.dir === 'asc' ? 1 : -1;
return [...list].sort((a, b) => {
if (sort.field === 'name') {
const na = repairUtf8Mojibake(a.name ?? '');
const nb = repairUtf8Mojibake(b.name ?? '');
return mul * na.localeCompare(nb, 'ko');
}
return mul * (strategyCreatedTimeMs(a) - strategyCreatedTimeMs(b));
});
}
export function strategyListSortLabel(sort: StrategyListSort, field: StrategyListSortField): string {
if (sort.field !== field) {
return field === 'name' ? '이름순' : '최신순';
}
if (field === 'name') {
return sort.dir === 'asc' ? '이름순 ↑' : '이름순 ↓';
}
return sort.dir === 'desc' ? '최신순 ↓' : '최신순 ↑';
}