feat: add Holo-work recommendations and responsive member grids
Publish GitHub Pages output / publish (push) Successful in 30s

This commit is contained in:
2026-09-18 12:38:59 +09:00
parent ba21e8f1c1
commit d941a12b4e
5 changed files with 154 additions and 21 deletions
+29 -2
View File
@@ -1,11 +1,38 @@
export type HoloWorkSort = 'member' | 'asc' | 'desc';
export type HoloWorkSort = 'member' | 'asc' | 'desc' | 'achievement';
type Thresholds = Readonly<Record<string, readonly number[]>>;
export function remainingWork(id: string, counts: Readonly<Record<string, number>>, thresholds: Thresholds): number {
const count = counts[id] ?? 0;
const target = thresholds[id]?.find((value) => value > count);
return target === undefined ? Infinity : target - count;
}
/** A fresh ordering snapshot; subsequent count edits do not move its rows. */
export function sortHoloWorkMembers<T extends { id: string; order: number }>(
members: readonly T[], counts: Readonly<Record<string, number>>, sort: HoloWorkSort
members: readonly T[], counts: Readonly<Record<string, number>>, sort: HoloWorkSort, thresholds: Thresholds = {}
): T[] {
return [...members].sort((a, b) => {
const difference = (counts[a.id] ?? 0) - (counts[b.id] ?? 0);
if (sort === 'achievement') {
return (remainingWork(a.id, counts, thresholds) - remainingWork(b.id, counts, thresholds)) || difference || a.order - b.order;
}
return (sort === 'asc' ? difference : sort === 'desc' ? -difference : 0) || a.order - b.order;
});
}
/** Keep pinned slots and fill the others with distinct members with unfinished achievements. */
export function recommendWorkMembers<T extends { id: string; order: number }>(
members: readonly T[], counts: Readonly<Record<string, number>>, thresholds: Thresholds,
selected: readonly string[], pinned: readonly boolean[]
): string[] {
const used = new Set<string>();
const result = Array.from({ length: 5 }, (_, index) => {
const id = selected[index];
if (!pinned[index] || !members.some((member) => member.id === id) || used.has(id)) return '';
used.add(id);
return id;
});
const candidates = sortHoloWorkMembers(members, counts, 'achievement', thresholds)
.filter((member) => !used.has(member.id) && Number.isFinite(remainingWork(member.id, counts, thresholds)));
return result.map((id) => id || candidates.shift()?.id || '');
}