Skip to content

Commit 20e1f3e

Browse files
committed
🎨 Enable to update submission status from drop down in task list by grades and detailed workbook page (#1858)
1 parent 61a63c4 commit 20e1f3e

File tree

5 files changed

+88
-60
lines changed

5 files changed

+88
-60
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<script lang="ts">
2+
import { TableBodyCell } from 'svelte-5-ui-lib';
3+
4+
import type { TaskResult } from '$lib/types/task';
5+
6+
import SubmissionStatusImage from '$lib/components/SubmissionStatus/SubmissionStatusImage.svelte';
7+
import UpdatingDropdown from '$lib/components/SubmissionStatus/UpdatingDropdown.svelte';
8+
9+
interface Props {
10+
taskResult: TaskResult;
11+
isLoggedIn: boolean;
12+
onupdate?: (updatedTask: TaskResult) => void; // Ensure to update task result in parent component.
13+
}
14+
15+
let { taskResult, isLoggedIn, onupdate = () => {} }: Props = $props();
16+
17+
let updatingDropdown: UpdatingDropdown;
18+
</script>
19+
20+
<TableBodyCell
21+
class="items-center justify-center w-20 px-0 pt-4 sm:pt-4 pb-0 sm:pb-1"
22+
onclick={() => updatingDropdown.toggle()}
23+
>
24+
<div class="flex items-center justify-center min-w-[80px] max-w-[80px]">
25+
<SubmissionStatusImage {taskResult} {isLoggedIn} />
26+
</div>
27+
</TableBodyCell>
28+
29+
<UpdatingDropdown bind:this={updatingDropdown} {taskResult} {isLoggedIn} {onupdate} />

src/lib/components/SubmissionStatus/UpdatingDropdown.svelte

+12-9
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
taskResult: TaskResult;
1010
isLoggedIn: boolean;
1111
onupdate?: (updatedTask: TaskResult) => void;
12+
dropdownClass?: string;
1213
}
1314
14-
let { taskResult, isLoggedIn, onupdate = () => {} }: Props = $props();
15+
let { taskResult, isLoggedIn, onupdate, dropdownClass = '' }: Props = $props();
1516
1617
let updatingDropdown: UpdatingDropdown;
1718
</script>
@@ -44,9 +45,10 @@
4445
taskResult: TaskResult;
4546
isLoggedIn: boolean;
4647
onupdate: (updatedTask: TaskResult) => void; // Ensure to update task result in parent component.
48+
dropdownClass?: string;
4749
}
4850
49-
let { taskResult, isLoggedIn, onupdate }: Props = $props();
51+
let { taskResult, isLoggedIn, onupdate, dropdownClass = '' }: Props = $props();
5052
5153
const { page } = getStores();
5254
let activeUrl = $state($page.url.pathname);
@@ -67,11 +69,7 @@
6769
let selectedSubmissionStatus = $state<SubmissionStatus>();
6870
let showForm = $state(false);
6971
70-
function handleClick(submissionStatus: {
71-
innerId: string;
72-
innerName: string;
73-
labelName: string;
74-
}): void {
72+
function handleClick(submissionStatus: SubmissionStatus): void {
7573
selectedSubmissionStatus = submissionStatus;
7674
showForm = true;
7775
@@ -92,6 +90,7 @@
9290
innerId: string;
9391
innerName: string;
9492
labelName: string;
93+
imagePath: string;
9594
};
9695
9796
type EnhanceForSubmit = {
@@ -158,8 +157,10 @@
158157
...taskResult,
159158
status_name: submissionStatus.innerName,
160159
status_id: submissionStatus.innerId,
160+
submission_status_image_path: submissionStatus.imagePath,
161161
submission_status_label_name: submissionStatus.labelName,
162-
is_ac: submissionStatus.innerName === 'ac',
162+
is_ac:
163+
submissionStatus.innerName === 'ac' || submissionStatus.innerName === 'ac_with_editorial',
163164
updated_at: new Date(),
164165
};
165166
}
@@ -170,17 +171,19 @@
170171
innerId: status.id,
171172
innerName: status.status_name,
172173
labelName: status.label_name,
174+
imagePath: status.image_path,
173175
};
174176
return option;
175177
});
176178
</script>
177179

178180
<div class="relative">
181+
<!-- HACK: classの設定は、テーブルの上部・下部をクリックしたときに、いずれもドロップダウンを操作できるようにするための苦肉の策 -->
179182
<Dropdown
180183
{activeUrl}
181184
{dropdownStatus}
182185
{closeDropdown}
183-
class="absolute w-32 z-20 left-auto right-0 mt-8"
186+
class="absolute w-32 z-50 top-12 -translate-y-full {dropdownClass}"
184187
>
185188
<DropdownUl>
186189
{#if isLoggedIn}

src/lib/components/TaskList.svelte

+20-21
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@
1313
import { type TaskResult, type TaskResults, TaskGrade } from '$lib/types/task';
1414
1515
import ThermometerProgressBar from '$lib/components/ThermometerProgressBar.svelte';
16-
import UpdatingModal from '$lib/components/SubmissionStatus/UpdatingModal.svelte';
17-
import SubmissionStatusImage from '$lib/components/SubmissionStatus/SubmissionStatusImage.svelte';
18-
import ExternalLinkWrapper from '$lib/components/ExternalLinkWrapper.svelte';
1916
import AcceptedCounter from '$lib/components/SubmissionStatus/AcceptedCounter.svelte';
17+
import SubmissionStatusTableBodyCell from '$lib/components/SubmissionStatus/SubmissionStatusInTableBodyCell.svelte';
18+
import ExternalLinkWrapper from '$lib/components/ExternalLinkWrapper.svelte';
2019
2120
import { getBackgroundColorFrom } from '$lib/services/submission_status';
2221
@@ -33,15 +32,18 @@
3332
3433
let { grade, gradeColor, taskResults, isAdmin, isLoggedIn }: Props = $props();
3534
36-
// TODO: 他のコンポーネントでも利用できるようにする。
37-
let updatingModal: UpdatingModal | null = null;
35+
function updateTaskResult(updatedTask: TaskResult): void {
36+
const newTaskResults = [...taskResults];
37+
38+
const index = newTaskResults.findIndex(
39+
(task: TaskResult) => task.task_id === updatedTask.task_id,
40+
);
3841
39-
function openModal(taskResult: TaskResult): void {
40-
if (updatingModal) {
41-
updatingModal.openModal(taskResult);
42-
} else {
43-
console.error('Failed to initialize UpdatingModal component.');
42+
if (index !== -1) {
43+
newTaskResults[index] = updatedTask;
4444
}
45+
46+
taskResults = newTaskResults;
4547
}
4648
</script>
4749

@@ -66,7 +68,6 @@
6668
</span>
6769
{/snippet}
6870

69-
<!-- FIXME: clickを1回実行するとactionsが2回実行されてしまう。原因と修正方法が分かっていない。 -->
7071
<!-- TODO: 問題が多くなってきたら、ページネーションを導入する -->
7172
<!-- TODO: 回答状況に応じて、フィルタリングできるようにする -->
7273
<div class="overflow-auto rounded-md border">
@@ -90,14 +91,14 @@
9091
id={taskResult.contest_id + '-' + taskResult.task_id}
9192
class={getBackgroundColorFrom(taskResult.status_name)}
9293
>
93-
<TableBodyCell
94-
class="justify-center w-20 px-1 sm:px-3 pt-1 sm:pt-3 pb-0.5 sm:pb-1"
95-
onclick={() => openModal(taskResult)}
96-
>
97-
<div class="flex items-center justify-center w-full h-full">
98-
<SubmissionStatusImage {taskResult} {isLoggedIn} />
99-
</div>
100-
</TableBodyCell>
94+
<div class="px-1 sm:px-3">
95+
<SubmissionStatusTableBodyCell
96+
{taskResult}
97+
{isLoggedIn}
98+
onupdate={(updatedTask: TaskResult) => updateTaskResult(updatedTask)}
99+
/>
100+
</div>
101+
101102
<TableBodyCell class="w-1/2 text-left truncate pl-0 sm:pl-6">
102103
<ExternalLinkWrapper
103104
url={getTaskUrl(taskResult.contest_id, taskResult.task_id)}
@@ -135,5 +136,3 @@
135136
</div>
136137
</AccordionItem>
137138
</Accordion>
138-
139-
<UpdatingModal bind:this={updatingModal} {isLoggedIn} />

src/lib/components/TaskTables/TaskTableBodyCell.svelte

+7-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
<EllipsisVertical class="w-4 h-4 mx-auto" />
6666
</button>
6767

68-
<UpdatingDropdown bind:this={updatingDropdown} {taskResult} {isLoggedIn} {onupdate} />
68+
<UpdatingDropdown
69+
bind:this={updatingDropdown}
70+
{taskResult}
71+
{isLoggedIn}
72+
{onupdate}
73+
dropdownClass="left-auto right-0 mt-8"
74+
/>
6975
</div>
7076
{/snippet}

src/routes/workbooks/[slug]/+page.svelte

+20-29
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
import PublicationStatusLabel from '$lib/components/WorkBooks/PublicationStatusLabel.svelte';
1414
import CompletedTasks from '$lib/components/Trophies/CompletedTasks.svelte';
1515
import HeadingOne from '$lib/components/HeadingOne.svelte';
16-
import UpdatingModal from '$lib/components/SubmissionStatus/UpdatingModal.svelte';
17-
import SubmissionStatusImage from '$lib/components/SubmissionStatus/SubmissionStatusImage.svelte';
1816
import GradeLabel from '$lib/components/GradeLabel.svelte';
17+
import SubmissionStatusTableBodyCell from '$lib/components/SubmissionStatus/SubmissionStatusInTableBodyCell.svelte';
1918
import ExternalLinkWrapper from '$lib/components/ExternalLinkWrapper.svelte';
2019
import CommentAndHint from '$lib/components/WorkBook/CommentAndHint.svelte';
2120
@@ -44,6 +43,17 @@
4443
return taskResults?.get(taskId) as TaskResult;
4544
};
4645
46+
const updateTaskResult = (updatedTask: TaskResult): void => {
47+
const taskId = updatedTask.task_id;
48+
49+
if (taskResults.has(taskId)) {
50+
// Force to update the task result.
51+
const newTaskResults = new Map(taskResults);
52+
newTaskResults.set(taskId, updatedTask);
53+
taskResults = newTaskResults;
54+
}
55+
};
56+
4757
const getTaskGrade = (taskId: string): TaskGrade => {
4858
return getTaskResult(taskId)?.grade as TaskGrade;
4959
};
@@ -75,19 +85,6 @@
7585
return getContestIdFrom(taskId) + '-' + taskId;
7686
};
7787
78-
// HACK:: `updatingModal` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates.
79-
// eslint-disable-next-line svelte/valid-compile
80-
let updatingModal: UpdatingModal | null = null;
81-
82-
// HACK: clickを1回実行するとactionsが2回実行されてしまう。原因と修正方法が分かっていない。
83-
function handleClick(taskId: string) {
84-
if (updatingModal) {
85-
updatingModal.openModal(getTaskResult(taskId));
86-
} else {
87-
console.error('Failed to initialize UpdatingModal component.');
88-
}
89-
}
90-
9188
$effect(() => {
9289
if (taskResults && workBook && Array.isArray(workBook.workBookTasks)) {
9390
workBookTasks = workBook.workBookTasks;
@@ -173,18 +170,14 @@
173170
</div>
174171
</TableBodyCell>
175172

176-
<!-- 回答状況の更新 -->
177-
<TableBodyCell
178-
class="justify-center w-20 px-0 pt-1 sm:pt-3 pb-0.5 sm:pb-1"
179-
onclick={() => handleClick(workBookTask.taskId)}
180-
>
181-
<div class="flex items-center justify-center min-w-[80px] max-w-[80px]">
182-
<SubmissionStatusImage
183-
taskResult={getTaskResult(workBookTask.taskId)}
184-
{isLoggedIn}
185-
/>
186-
</div>
187-
</TableBodyCell>
173+
<!-- 回答状況 -->
174+
<div class="">
175+
<SubmissionStatusTableBodyCell
176+
taskResult={getTaskResult(workBookTask.taskId)}
177+
{isLoggedIn}
178+
onupdate={(updatedTask: TaskResult) => updateTaskResult(updatedTask)}
179+
/>
180+
</div>
188181

189182
<!-- 問題のリンク -->
190183
<TableBodyCell class="w-1/2 px-3 sm:px-6">
@@ -219,8 +212,6 @@
219212
</TableBody>
220213
</Table>
221214
</div>
222-
223-
<UpdatingModal bind:this={updatingModal} {isLoggedIn} />
224215
{:else}
225216
{'問題を1問以上登録してください。'}
226217
{/if}

0 commit comments

Comments
 (0)