Skip to content

Commit

Permalink
feat: optimize assignment select ui (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
Okabe-Rintarou-0 committed Apr 26, 2024
1 parent 596eaf8 commit 76d548d
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 32 deletions.
29 changes: 27 additions & 2 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { decode } from "js-base64";
import { getConfig } from "./store";
import { Dayjs } from "dayjs"
import { Attachment, File as FileModel } from "./model";
import dayjs, { Dayjs } from "dayjs"
import { Assignment, AssignmentDate, Attachment, File as FileModel } from "./model";

export function formatDate(inputDate: string | undefined | null): string {
if (!inputDate) {
Expand Down Expand Up @@ -113,4 +113,29 @@ export function attachmentToFile(attachment: Attachment) {
size: attachment.size,
mime_class: attachment.mime_class,
} as FileModel;
}

export function getBaseDate(dates: AssignmentDate[]) {
return dates.find(date => date.base);
}

export function assignmentIsEnded(assignment: Assignment) {
const baseDate = getBaseDate(assignment.all_dates);
const dued = dayjs(baseDate?.due_at).isBefore(dayjs());
const locked = dayjs(baseDate?.lock_at).isBefore(dayjs());
return dued || locked;
}

export function assignmentIsNotUnlocked(assignment: Assignment) {
const baseDate = getBaseDate(assignment.all_dates);
if (!baseDate?.unlock_at) {
return false;
}
const locked = dayjs(baseDate.unlock_at).isAfter(dayjs());
return locked;
}

export function assignmentNotNeedSubmit(assignment: Assignment) {
return !assignment.submission ||
assignment.submission_types.includes("none") || assignment.submission_types.includes("not_graded");
}
18 changes: 2 additions & 16 deletions src/page/assignments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Avatar, Button, Checkbox, CheckboxProps, Divider, List, Space, Table, T
import BasicLayout from "../components/layout";
import useMessage from "antd/es/message/useMessage";
import { useEffect, useState } from "react";
import { Assignment, AssignmentDate, Attachment, Course, GradeStatus, Submission, User } from "../lib/model";
import { Assignment, Attachment, Course, GradeStatus, Submission, User } from "../lib/model";
import { invoke } from "@tauri-apps/api";
import { attachmentToFile, formatDate } from "../lib/utils";
import { assignmentIsEnded, assignmentNotNeedSubmit, attachmentToFile, formatDate, getBaseDate } from "../lib/utils";
import CourseSelect from "../components/course_select";
import { usePreview } from "../lib/hooks";
import dayjs from "dayjs";
Expand Down Expand Up @@ -95,20 +95,6 @@ export default function AssignmentsPage() {
}
}

const assignmentIsEnded = (assignment: Assignment) => {
const baseDate = getBaseDate(assignment.all_dates);
const dued = dayjs(baseDate?.due_at).isBefore(dayjs());
const locked = dayjs(baseDate?.lock_at).isBefore(dayjs());
return dued || locked;
}

const assignmentNotNeedSubmit = (assignment: Assignment) => {
return !assignment.submission ||
assignment.submission_types.includes("none") || assignment.submission_types.includes("not_graded");
}

const getBaseDate = (dates: AssignmentDate[]) => dates.find(date => date.base);

const getColumns = () => {
const columns = [{
title: '作业名',
Expand Down
21 changes: 12 additions & 9 deletions src/page/qrcode.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Card, Image, Space } from "antd";
import { Card, Image, List, Space } from "antd";
import BasicLayout from "../components/layout";
import CourseSelect from "../components/course_select";
import { useEffect, useState } from "react";
Expand Down Expand Up @@ -62,14 +62,17 @@ export function QRCodePage() {
<CourseSelect onChange={handleCourseSelect} disabled={operating} courses={courses} />
{
scanResults.length > 0 &&
scanResults.map(scanResult => {
return <Card
hoverable
cover={<Image src={scanResult.file.url} />}
>
<Meta title={scanResult.file.display_name} />
</Card>;
})
<List grid={{ gutter: 16, column: 4 }} style={{ width: "100%" }} dataSource={scanResults}
renderItem={scanResult => <List.Item>
<Card
hoverable
cover={<Image src={scanResult.file.url} />}
>
<Meta title={scanResult.file.display_name} />
</Card>

</List.Item>}>
</List>
}
{scanResults.length === 0 && <Empty />}
</Space>
Expand Down
54 changes: 49 additions & 5 deletions src/page/submissions.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { Button, Input, Select, Space, Table, Tag } from "antd";
import BasicLayout from "../components/layout";
import useMessage from "antd/es/message/useMessage";
import { ReactNode, useEffect, useState } from "react";
import { ReactNode, useEffect, useMemo, useState } from "react";
import { Assignment, Attachment, Course, FileDownloadTask, GradeStatistic, Submission, User } from "../lib/model";
import { invoke } from "@tauri-apps/api";
import { attachmentToFile, formatDate } from "../lib/utils";
import { assignmentIsNotUnlocked, attachmentToFile, formatDate } from "../lib/utils";
import CourseSelect from "../components/course_select";
import FileDownloadTable from "../components/file_download_table";
import GradeStatisticChart from "../components/grade_statistic";
import { usePreview } from "../lib/hooks";
import CommentPanel from "../components/comment_panel";
import { WarningOutlined } from "@ant-design/icons"

export default function SubmissionsPage() {
const [messageApi, contextHolder] = useMessage();
Expand All @@ -24,13 +25,14 @@ export default function SubmissionsPage() {
const [downloadTasks, setDownloadTasks] = useState<FileDownloadTask[]>([]);
const [selectedAssignment, setSelectedAssignment] = useState<Assignment | undefined>(undefined);
const [selectedAttachments, setSelectedAttachments] = useState<Attachment[]>([]);
const usersMap = new Map<number, User>(users.map(user => ([user.id, user])));
const usersMap = useMemo(() => new Map<number, User>(users.map(user => ([user.id, user]))), [users]);
const [statistic, setStatistic] = useState<GradeStatistic | undefined>(undefined);
const [keyword, setKeyword] = useState<string>("");
const [attachmentToComment, setAttachmentToComment] = useState<number>(-1);
const [expandedRowKeys, setExpandedRowKeys] = useState<number[]>([]);
const [previewFooter, setPreviewFooter] = useState<ReactNode>(undefined);
const [commentingWhilePreviewing, setCommentingWhilePreviewing] = useState<boolean>(false);
const [notSubmitStudents, setNotSubmitStudents] = useState<User[]>([]);

const refreshSubmission = async (studentId: number) => {
const submission = await invoke("get_single_course_assignment_submission", {
Expand Down Expand Up @@ -94,6 +96,12 @@ export default function SubmissionsPage() {
setEntries(attachments.map(attachmentToFile));
}, [attachments]);

useEffect(() => {
if (attachments.length > 0) {
setNotSubmitStudents(getNotSubmitStudents());
}
}, [attachments])

const validateGrade = (grade: string) => {
if (grade.length === 0) {
return true;
Expand Down Expand Up @@ -309,7 +317,6 @@ export default function SubmissionsPage() {

const handleAssignmentSelect = (assignmentId: number) => {
setStatistic(undefined);
setAttachments([]);
setSelectedAttachments([]);
let assignment = assignments.find(assignment => assignment.id === assignmentId);
if (assignment) {
Expand Down Expand Up @@ -341,8 +348,35 @@ export default function SubmissionsPage() {
}
}

const getNotSubmitStudents = () => {
const notSubmitStudentsMap = new Map<number, User>();
usersMap.forEach(user => {
notSubmitStudentsMap.set(user.id, user);
});
attachments.forEach(attachment => {
notSubmitStudentsMap.delete(attachment.user_id);
});
return [...notSubmitStudentsMap.values()].filter(student => student.name !== "测验学生");
}

const getAssignmentTag = (assignment: Assignment) => {
const count = assignment.needs_grading_count ?? 0;
const notUnlocked = assignmentIsNotUnlocked(assignment);
if (notUnlocked) {
return <Tag color="geekblue">尚未解锁</Tag>
}
if (count === 0) {
return <Tag color="success">暂无待批改</Tag>
}

return <Tag color="warning" icon={<WarningOutlined />}>{count}份待批改</Tag>
}

const assignmentOptions = assignments.map(assignment => ({
label: assignment.name,
label: <Space>
<span>{assignment.name}</span>
{getAssignmentTag(assignment)}
</Space>,
value: assignment.id,
}));

Expand Down Expand Up @@ -372,6 +406,16 @@ export default function SubmissionsPage() {
selectedAssignment?.points_possible &&
<span>满分:<b>{selectedAssignment.points_possible}</b></span>
}
{
attachments.length > 0 && notSubmitStudents.length > 0 && <Space wrap>
未提交学生: {notSubmitStudents.map(s => <Tag>{s.name}</Tag>)}
</Space>
}
{
attachments.length === 0 && <Space wrap>
未提交学生: <Tag>暂无任何提交</Tag>
</Space>
}
{statistic && <GradeStatisticChart statistic={statistic} />}
<Input.Search placeholder="输入学生姓名关键词" onSearch={setKeyword} />
<Table style={{ width: "100%" }}
Expand Down

0 comments on commit 76d548d

Please sign in to comment.