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
9 changes: 9 additions & 0 deletions .claude/rules/coding-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ When the same constraint is enforced in two layers (e.g. Zod validation + SQL `C
.refine(...)
```

## Optimistic Updates

Derive computed fields (flags, labels, etc.) from the canonical data source — don't
re-implement the derivation inline. Divergence causes a "works after reload" bug where
the server state is correct but the client-side update is wrong.

**Diagnostic**: "Not reflected live, but fixed after reload" → suspect the optimistic
update payload, not the reactivity system.

## Async Rollback: Capture State Before `await`

Capture `$state` values before the first `await` for safe rollback. A concurrent update can overwrite the variable while awaiting:
Expand Down
24 changes: 20 additions & 4 deletions src/features/tasks/components/contest-table/TaskTable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
} from '$features/tasks/utils/contest-table/contest_table_provider';

import { getBackgroundColorFrom } from '$lib/services/submission_status';
import { areAllTasksAccepted } from '$lib/utils/task';
import { createContestTaskPairKey } from '$lib/utils/contest_task_pair';

interface Props {
Expand Down Expand Up @@ -54,7 +55,7 @@
innerTaskTable: Record<string, Record<string, TaskResult>>;
headerIds: Array<string>;
contestIds: Array<string>;
metadata: any;

Check warning on line 58 in src/features/tasks/components/contest-table/TaskTable.svelte

View workflow job for this annotation

GitHub Actions / build (24)

Unexpected any. Specify a different type
displayConfig: ContestTableDisplayConfig;
}

Expand Down Expand Up @@ -105,6 +106,24 @@
return totalColumns > 8 ? 'flex flex-wrap' : 'flex flex-wrap xl:table-row';
}

function getRoundLabelClasses(contestTable: ProviderData, contestId: string): string {
const tasks = Object.values(contestTable.innerTaskTable[contestId]);
const bgColor = getRoundLabelBgColor(tasks);

return `w-full ${contestTable.displayConfig.roundLabelWidth} truncate px-2 py-2 text-center dark:text-gray-300 ${bgColor}`;
}

// Note: If any task is ac_with_editorial, that color takes priority over AC (Accepted).
function getRoundLabelBgColor(tasks: TaskResults): string {
if (!isLoggedIn || !areAllTasksAccepted(tasks, tasks)) {
return 'bg-gray-50 dark:bg-gray-800';
}

const hasEditorial = tasks.some((task) => task.status_name === 'ac_with_editorial');

return getBackgroundColorFrom(hasEditorial ? 'ac_with_editorial' : 'ac');
}

function getBodyCellClasses(taskResult: TaskResult, tableBodyCellWidth: string): string {
const backgroundColor = getBackgroundColor(taskResult);

Expand Down Expand Up @@ -171,7 +190,7 @@
<!-- https://flowbite-svelte.com/docs/components/buttons -->
<div class="flex justify-center md:justify-start m-4">
<div class="flex flex-wrap justify-start gap-1 shadow-none">
{#each Object.entries(contestTableProviderGroups) as [type, config]}

Check warning on line 193 in src/features/tasks/components/contest-table/TaskTable.svelte

View workflow job for this annotation

GitHub Actions / build (24)

Each block should have a key
<Button
onclick={() => updateActiveContestType(type as ContestTableProviderGroups)}
color="alternative"
Expand Down Expand Up @@ -240,10 +259,7 @@
{#each contestTable.contestIds as contestId (contestId)}
<TableBodyRow class={getBodyRowClasses(totalColumns)}>
{#if contestTable.displayConfig.isShownRoundLabel}
<TableBodyCell
class="w-full {contestTable.displayConfig
.roundLabelWidth} truncate px-2 py-2 text-center bg-gray-50 dark:bg-gray-800"
>
<TableBodyCell class={getRoundLabelClasses(contestTable, contestId)}>
{getContestRoundLabel(provider, contestId)}
</TableBodyCell>
{/if}
Expand Down
4 changes: 3 additions & 1 deletion src/lib/components/SubmissionStatus/UpdatingDropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@
status_name: submissionStatus.innerName,
status_id: submissionStatus.innerId,
submission_status_label_name: submissionStatus.labelName,
is_ac: submissionStatus.innerName === 'ac',
is_ac:
submission_statuses.find((status) => status.status_name === submissionStatus.innerName)
?.is_AC ?? false,
updated_at: new Date(),
};
}
Expand Down Expand Up @@ -196,7 +198,7 @@
class="w-32 z-50 border border-gray-200 dark:border-gray-100"
>
{#if isLoggedIn}
{#each submissionStatusOptions as submissionStatus}

Check warning on line 201 in src/lib/components/SubmissionStatus/UpdatingDropdown.svelte

View workflow job for this annotation

GitHub Actions / build (24)

Each block should have a key
<DropdownItem onclick={() => handleClick(submissionStatus)} class="rounded-md">
<div
class="flex items-center justify-between w-full text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white"
Expand Down
Loading