Skip to content

Commit 0cfdb09

Browse files
river0525claude
andcommitted
fix: address CodeRabbit findings (round 1)
- Widen compareByContestIdAndTaskId signature to accept any object with contest_id/task_table_index, removing double cast in +page.svelte - Replace find() loop with Map in buildDonutSegments (O(n+m)) - Extract segLabel {@const} in VoteDonutChart to remove duplicate ternary - Add aria-label to FlaskConical in VotableGrade - Add aria-label to external link icon in votes list page - Add qGrades/dGrades tests to grade_options.test.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 67b38ff commit 0cfdb09

6 files changed

Lines changed: 55 additions & 11 deletions

File tree

src/features/votes/components/VotableGrade.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@
161161
</button>
162162

163163
{#if isProvisional}
164-
<FlaskConical class="w-3.5 h-3.5 shrink-0 text-gray-400 dark:text-gray-500" />
164+
<FlaskConical
165+
class="w-3.5 h-3.5 shrink-0 text-gray-400 dark:text-gray-500"
166+
aria-label="暫定グレード"
167+
/>
165168
{/if}
166169
</div>
167170

src/features/votes/components/VoteDonutChart.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
{#each segments as seg (seg.grade)}
8585
{@const lx = CX + RING_MID_RADIUS * Math.cos(seg.midAngle)}
8686
{@const ly = CY + RING_MID_RADIUS * Math.sin(seg.midAngle)}
87+
{@const segLabel = seg.grade === votedGrade ? `✅ ${seg.label}` : seg.label}
8788
{#if seg.pct >= 10}
8889
<text
8990
x={lx}
@@ -95,7 +96,7 @@
9596
stroke-width="2.5"
9697
paint-order="stroke"
9798
font-size="11"
98-
font-weight="bold">{seg.grade === votedGrade ? `✅ ${seg.label}` : seg.label}</text
99+
font-weight="bold">{segLabel}</text
99100
>
100101
<text
101102
x={lx}
@@ -119,7 +120,7 @@
119120
stroke-width="2.5"
120121
paint-order="stroke"
121122
font-size="11"
122-
font-weight="bold">{seg.grade === votedGrade ? `✅ ${seg.label}` : seg.label}</text
123+
font-weight="bold">{segLabel}</text
123124
>
124125
{/if}
125126
{/each}

src/features/votes/utils/donut_chart.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ export function buildDonutSegments(
4040
return [];
4141
}
4242

43+
const countMap = new Map(counters.map((c) => [c.grade, c.count]));
4344
let cumulative = 0;
4445
const segments: DonutSegment[] = [];
4546

4647
for (const grade of grades) {
47-
const count = counters.find((c) => c.grade === grade)?.count ?? 0;
48+
const count = countMap.get(grade) ?? 0;
4849
if (count === 0) {
4950
continue;
5051
}

src/features/votes/utils/grade_options.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
22

33
import { TaskGrade, taskGradeValues } from '$lib/types/task';
44

5-
import { nonPendingGrades } from './grade_options';
5+
import { nonPendingGrades, qGrades, dGrades } from './grade_options';
66

77
describe('nonPendingGrades', () => {
88
it('does not include PENDING', () => {
@@ -19,3 +19,41 @@ describe('nonPendingGrades', () => {
1919
expect(nonPendingGrades[nonPendingGrades.length - 1]).toBe(TaskGrade.D6);
2020
});
2121
});
22+
23+
describe('qGrades', () => {
24+
it('contains only Q-tier grades', () => {
25+
expect(qGrades.every((g) => g.startsWith('Q'))).toBe(true);
26+
});
27+
28+
it('contains no D-tier grades', () => {
29+
expect(qGrades.some((g) => g.startsWith('D'))).toBe(false);
30+
});
31+
32+
it('equals nonPendingGrades filtered to Q prefix', () => {
33+
expect(qGrades).toEqual(nonPendingGrades.filter((g) => g.startsWith('Q')));
34+
});
35+
36+
it('starts with Q11 and ends with Q1', () => {
37+
expect(qGrades[0]).toBe(TaskGrade.Q11);
38+
expect(qGrades[qGrades.length - 1]).toBe(TaskGrade.Q1);
39+
});
40+
});
41+
42+
describe('dGrades', () => {
43+
it('contains only D-tier grades', () => {
44+
expect(dGrades.every((g) => g.startsWith('D'))).toBe(true);
45+
});
46+
47+
it('contains no Q-tier grades', () => {
48+
expect(dGrades.some((g) => g.startsWith('Q'))).toBe(false);
49+
});
50+
51+
it('equals nonPendingGrades filtered to D prefix', () => {
52+
expect(dGrades).toEqual(nonPendingGrades.filter((g) => g.startsWith('D')));
53+
});
54+
55+
it('starts with D1 and ends with D6', () => {
56+
expect(dGrades[0]).toBe(TaskGrade.D1);
57+
expect(dGrades[dGrades.length - 1]).toBe(TaskGrade.D6);
58+
});
59+
});

src/lib/utils/task.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@ export const areAllTasksAccepted = (
7272

7373
// See:
7474
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
75-
export function compareByContestIdAndTaskId(first: TaskResult, second: TaskResult): number {
75+
export function compareByContestIdAndTaskId(
76+
first: { contest_id: string; task_table_index: string },
77+
second: { contest_id: string; task_table_index: string },
78+
): number {
7679
const firstContestPriority = getContestPriority(first.contest_id);
7780
const secondContestPriority = getContestPriority(second.contest_id);
7881

src/routes/votes/+page.svelte

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import { getTaskUrl } from '$lib/utils/task';
2020
import { getContestNameLabel } from '$lib/utils/contest';
2121
import { compareByContestIdAndTaskId } from '$lib/utils/task';
22-
import type { TaskResult } from '$lib/types/task';
2322
2423
const MAX_SEARCH_RESULTS = 20;
2524
@@ -37,9 +36,7 @@
3736
(t.task_id ?? '').toLowerCase().includes(search.toLowerCase()) ||
3837
(t.contest_id ?? '').toLowerCase().includes(search.toLowerCase()),
3938
)
40-
.sort((a, b) =>
41-
compareByContestIdAndTaskId(a as unknown as TaskResult, b as unknown as TaskResult),
42-
)
39+
.sort((a, b) => compareByContestIdAndTaskId(a, b))
4340
.slice(0, MAX_SEARCH_RESULTS),
4441
);
4542
</script>
@@ -107,9 +104,10 @@
107104
href={getTaskUrl(task.contest_id, task.task_id)}
108105
target="_blank"
109106
rel="noreferrer external"
107+
aria-label={`${task.title} を別タブで開く`}
110108
class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 shrink-0"
111109
>
112-
<ExternalLink class="w-3.5 h-3.5" />
110+
<ExternalLink class="w-3.5 h-3.5" aria-hidden="true" />
113111
</a>
114112
</div>
115113
</TableBodyCell>

0 commit comments

Comments
 (0)