Skip to content

🎨 Enable to update submission status from drop down in task list by grades and detailed workbook page (#1858) #1859

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: staging
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<script lang="ts">
import { TableBodyCell } from 'svelte-5-ui-lib';

import type { TaskResult } from '$lib/types/task';

import SubmissionStatusImage from '$lib/components/SubmissionStatus/SubmissionStatusImage.svelte';
import UpdatingDropdown from '$lib/components/SubmissionStatus/UpdatingDropdown.svelte';

interface Props {
taskResult: TaskResult;
isLoggedIn: boolean;
onupdate?: (updatedTask: TaskResult) => void; // Ensure to update task result in parent component.
}

let { taskResult, isLoggedIn, onupdate = () => {} }: Props = $props();

let updatingDropdown: UpdatingDropdown;
</script>

<TableBodyCell
class="items-center justify-center w-20 px-0 pt-4 sm:pt-4 pb-0 sm:pb-1"
onclick={() => updatingDropdown.toggle()}
>
<div class="flex items-center justify-center min-w-[80px] max-w-[80px]">
<SubmissionStatusImage {taskResult} {isLoggedIn} />
</div>
</TableBodyCell>

<UpdatingDropdown bind:this={updatingDropdown} {taskResult} {isLoggedIn} {onupdate} />
21 changes: 12 additions & 9 deletions src/lib/components/SubmissionStatus/UpdatingDropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
taskResult: TaskResult;
isLoggedIn: boolean;
onupdate?: (updatedTask: TaskResult) => void;
dropdownClass?: string;
}

let { taskResult, isLoggedIn, onupdate = () => {} }: Props = $props();
let { taskResult, isLoggedIn, onupdate, dropdownClass = '' }: Props = $props();

let updatingDropdown: UpdatingDropdown;
</script>
Expand Down Expand Up @@ -44,9 +45,10 @@
taskResult: TaskResult;
isLoggedIn: boolean;
onupdate: (updatedTask: TaskResult) => void; // Ensure to update task result in parent component.
dropdownClass?: string;
}

let { taskResult, isLoggedIn, onupdate }: Props = $props();
let { taskResult, isLoggedIn, onupdate, dropdownClass = '' }: Props = $props();

const { page } = getStores();
let activeUrl = $state($page.url.pathname);
Expand All @@ -67,11 +69,7 @@
let selectedSubmissionStatus = $state<SubmissionStatus>();
let showForm = $state(false);

function handleClick(submissionStatus: {
innerId: string;
innerName: string;
labelName: string;
}): void {
function handleClick(submissionStatus: SubmissionStatus): void {
selectedSubmissionStatus = submissionStatus;
showForm = true;

Expand All @@ -92,6 +90,7 @@
innerId: string;
innerName: string;
labelName: string;
imagePath: string;
};

type EnhanceForSubmit = {
Expand Down Expand Up @@ -158,8 +157,10 @@
...taskResult,
status_name: submissionStatus.innerName,
status_id: submissionStatus.innerId,
submission_status_image_path: submissionStatus.imagePath,
submission_status_label_name: submissionStatus.labelName,
is_ac: submissionStatus.innerName === 'ac',
is_ac:
submissionStatus.innerName === 'ac' || submissionStatus.innerName === 'ac_with_editorial',
updated_at: new Date(),
};
}
Expand All @@ -170,17 +171,19 @@
innerId: status.id,
innerName: status.status_name,
labelName: status.label_name,
imagePath: status.image_path,
};
return option;
});
</script>

<div class="relative">
<!-- HACK: classの設定は、テーブルの上部・下部をクリックしたときに、いずれもドロップダウンを操作できるようにするための苦肉の策 -->
<Dropdown
{activeUrl}
{dropdownStatus}
{closeDropdown}
class="absolute w-32 z-20 left-auto right-0 mt-8"
class="absolute w-32 z-50 top-12 -translate-y-full {dropdownClass}"
>
<DropdownUl>
{#if isLoggedIn}
Expand Down
102 changes: 0 additions & 102 deletions src/lib/components/SubmissionStatus/UpdatingModal.svelte

This file was deleted.

41 changes: 20 additions & 21 deletions src/lib/components/TaskList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
import { type TaskResult, type TaskResults, TaskGrade } from '$lib/types/task';

import ThermometerProgressBar from '$lib/components/ThermometerProgressBar.svelte';
import UpdatingModal from '$lib/components/SubmissionStatus/UpdatingModal.svelte';
import SubmissionStatusImage from '$lib/components/SubmissionStatus/SubmissionStatusImage.svelte';
import ExternalLinkWrapper from '$lib/components/ExternalLinkWrapper.svelte';
import AcceptedCounter from '$lib/components/SubmissionStatus/AcceptedCounter.svelte';
import SubmissionStatusTableBodyCell from '$lib/components/SubmissionStatus/SubmissionStatusInTableBodyCell.svelte';
import ExternalLinkWrapper from '$lib/components/ExternalLinkWrapper.svelte';

import { getBackgroundColorFrom } from '$lib/services/submission_status';

Expand All @@ -33,15 +32,18 @@

let { grade, gradeColor, taskResults, isAdmin, isLoggedIn }: Props = $props();

// TODO: 他のコンポーネントでも利用できるようにする。
let updatingModal: UpdatingModal | null = null;
function updateTaskResult(updatedTask: TaskResult): void {
const newTaskResults = [...taskResults];

const index = newTaskResults.findIndex(
(task: TaskResult) => task.task_id === updatedTask.task_id,
);

function openModal(taskResult: TaskResult): void {
if (updatingModal) {
updatingModal.openModal(taskResult);
} else {
console.error('Failed to initialize UpdatingModal component.');
if (index !== -1) {
newTaskResults[index] = updatedTask;
}

taskResults = newTaskResults;
}
</script>

Expand All @@ -66,7 +68,6 @@
</span>
{/snippet}

<!-- FIXME: clickを1回実行するとactionsが2回実行されてしまう。原因と修正方法が分かっていない。 -->
<!-- TODO: 問題が多くなってきたら、ページネーションを導入する -->
<!-- TODO: 回答状況に応じて、フィルタリングできるようにする -->
<div class="overflow-auto rounded-md border">
Expand All @@ -90,14 +91,14 @@
id={taskResult.contest_id + '-' + taskResult.task_id}
class={getBackgroundColorFrom(taskResult.status_name)}
>
<TableBodyCell
class="justify-center w-20 px-1 sm:px-3 pt-1 sm:pt-3 pb-0.5 sm:pb-1"
onclick={() => openModal(taskResult)}
>
<div class="flex items-center justify-center w-full h-full">
<SubmissionStatusImage {taskResult} {isLoggedIn} />
</div>
</TableBodyCell>
<div class="px-1 sm:px-3">
<SubmissionStatusTableBodyCell
{taskResult}
{isLoggedIn}
onupdate={(updatedTask: TaskResult) => updateTaskResult(updatedTask)}
/>
</div>

<TableBodyCell class="w-1/2 text-left truncate pl-0 sm:pl-6">
<ExternalLinkWrapper
url={getTaskUrl(taskResult.contest_id, taskResult.task_id)}
Expand Down Expand Up @@ -135,5 +136,3 @@
</div>
</AccordionItem>
</Accordion>

<UpdatingModal bind:this={updatingModal} {isLoggedIn} />
8 changes: 7 additions & 1 deletion src/lib/components/TaskTables/TaskTableBodyCell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@
<EllipsisVertical class="w-4 h-4 mx-auto" />
</button>

<UpdatingDropdown bind:this={updatingDropdown} {taskResult} {isLoggedIn} {onupdate} />
<UpdatingDropdown
bind:this={updatingDropdown}
{taskResult}
{isLoggedIn}
{onupdate}
dropdownClass="left-auto right-0 mt-8"
/>
</div>
{/snippet}
49 changes: 20 additions & 29 deletions src/routes/workbooks/[slug]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
import PublicationStatusLabel from '$lib/components/WorkBooks/PublicationStatusLabel.svelte';
import CompletedTasks from '$lib/components/Trophies/CompletedTasks.svelte';
import HeadingOne from '$lib/components/HeadingOne.svelte';
import UpdatingModal from '$lib/components/SubmissionStatus/UpdatingModal.svelte';
import SubmissionStatusImage from '$lib/components/SubmissionStatus/SubmissionStatusImage.svelte';
import GradeLabel from '$lib/components/GradeLabel.svelte';
import SubmissionStatusTableBodyCell from '$lib/components/SubmissionStatus/SubmissionStatusInTableBodyCell.svelte';
import ExternalLinkWrapper from '$lib/components/ExternalLinkWrapper.svelte';
import CommentAndHint from '$lib/components/WorkBook/CommentAndHint.svelte';

Expand Down Expand Up @@ -44,6 +43,17 @@
return taskResults?.get(taskId) as TaskResult;
};

const updateTaskResult = (updatedTask: TaskResult): void => {
const taskId = updatedTask.task_id;

if (taskResults.has(taskId)) {
// Force to update the task result.
const newTaskResults = new Map(taskResults);
newTaskResults.set(taskId, updatedTask);
taskResults = newTaskResults;
}
};

const getTaskGrade = (taskId: string): TaskGrade => {
return getTaskResult(taskId)?.grade as TaskGrade;
};
Expand Down Expand Up @@ -75,19 +85,6 @@
return getContestIdFrom(taskId) + '-' + taskId;
};

// HACK:: `updatingModal` is updated, but is not declared with `$state(...)`. Changing its value will not correctly trigger updates.
// eslint-disable-next-line svelte/valid-compile
let updatingModal: UpdatingModal | null = null;

// HACK: clickを1回実行するとactionsが2回実行されてしまう。原因と修正方法が分かっていない。
function handleClick(taskId: string) {
if (updatingModal) {
updatingModal.openModal(getTaskResult(taskId));
} else {
console.error('Failed to initialize UpdatingModal component.');
}
}

$effect(() => {
if (taskResults && workBook && Array.isArray(workBook.workBookTasks)) {
workBookTasks = workBook.workBookTasks;
Expand Down Expand Up @@ -173,18 +170,14 @@
</div>
</TableBodyCell>

<!-- 回答状況の更新 -->
<TableBodyCell
class="justify-center w-20 px-0 pt-1 sm:pt-3 pb-0.5 sm:pb-1"
onclick={() => handleClick(workBookTask.taskId)}
>
<div class="flex items-center justify-center min-w-[80px] max-w-[80px]">
<SubmissionStatusImage
taskResult={getTaskResult(workBookTask.taskId)}
{isLoggedIn}
/>
</div>
</TableBodyCell>
<!-- 回答状況 -->
<div class="">
<SubmissionStatusTableBodyCell
taskResult={getTaskResult(workBookTask.taskId)}
{isLoggedIn}
onupdate={(updatedTask: TaskResult) => updateTaskResult(updatedTask)}
/>
</div>
Comment on lines +173 to +180
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

TableBodyCell consistency issue

Unlike other cells in this table row, the SubmissionStatusTableBodyCell is wrapped in a regular div instead of a TableBodyCell component. This inconsistency could cause layout issues.

Apply this change to maintain consistent table structure:

-              <!-- 回答状況 -->
-              <div class="">
-                <SubmissionStatusTableBodyCell
-                  taskResult={getTaskResult(workBookTask.taskId)}
-                  {isLoggedIn}
-                  onupdate={(updatedTask: TaskResult) => updateTaskResult(updatedTask)}
-                />
-              </div>
+              <!-- 回答状況 -->
+              <TableBodyCell class="">
+                <SubmissionStatusTableBodyCell
+                  taskResult={getTaskResult(workBookTask.taskId)}
+                  {isLoggedIn}
+                  onupdate={(updatedTask: TaskResult) => updateTaskResult(updatedTask)}
+                />
+              </TableBodyCell>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<!-- 回答状況 -->
<div class="">
<SubmissionStatusTableBodyCell
taskResult={getTaskResult(workBookTask.taskId)}
{isLoggedIn}
onupdate={(updatedTask: TaskResult) => updateTaskResult(updatedTask)}
/>
</div>
<!-- 回答状況 -->
<TableBodyCell class="">
<SubmissionStatusTableBodyCell
taskResult={getTaskResult(workBookTask.taskId)}
{isLoggedIn}
onupdate={(updatedTask: TaskResult) => updateTaskResult(updatedTask)}
/>
</TableBodyCell>


<!-- 問題のリンク -->
<TableBodyCell class="w-1/2 px-3 sm:px-6">
Expand Down Expand Up @@ -219,8 +212,6 @@
</TableBody>
</Table>
</div>

<UpdatingModal bind:this={updatingModal} {isLoggedIn} />
{:else}
{'問題を1問以上登録してください。'}
{/if}
Expand Down
Loading